Merge branch 'cyclic-route-0.123.0'
This commit is contained in:
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. ..
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Full error traceback**
|
||||
Please provide the full error traceback/failed transaction/failed tenderly simulation
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,3 +1,40 @@
|
||||
## [0.128.0](https://github.com/propeller-heads/tycho-execution/compare/0.127.0...0.128.0) (2025-09-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Use tycho-common from crates.io ([89435b5](https://github.com/propeller-heads/tycho-execution/commit/89435b5c76bbd1fbf2c946686ede442c4647cd29))
|
||||
|
||||
## [0.127.0](https://github.com/propeller-heads/tycho-execution/compare/0.126.0...0.127.0) (2025-09-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Use tycho-common from github ([5d73e3b](https://github.com/propeller-heads/tycho-execution/commit/5d73e3b47c5cbff4fa0c88e71f15f48f3831f199))
|
||||
|
||||
## [0.126.0](https://github.com/propeller-heads/tycho-execution/compare/0.125.0...0.126.0) (2025-09-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add historical_trade option to encoding ([c51c6f5](https://github.com/propeller-heads/tycho-execution/commit/c51c6f52a5c1a7e47caab3bfa721f7c373a8229e))
|
||||
* Pass the file contents instead of the file path for executors ([e78a362](https://github.com/propeller-heads/tycho-execution/commit/e78a362894955a8b0e676bbcb189195d00815aad))
|
||||
* Update tycho common to point to hooks feature branch ([a98e8d2](https://github.com/propeller-heads/tycho-execution/commit/a98e8d21ccd2eafeb42805acb6ce157b60374a0c))
|
||||
|
||||
## [0.125.0](https://github.com/propeller-heads/tycho-execution/compare/0.124.0...0.125.0) (2025-09-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* bump tycho-common version ([fa3bb6d](https://github.com/propeller-heads/tycho-execution/commit/fa3bb6daf74c6df254a4b65718663ec0ca7339d6))
|
||||
|
||||
## [0.124.0](https://github.com/propeller-heads/tycho-execution/compare/0.123.0...0.124.0) (2025-09-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* bump tycho-common version ([bbd732d](https://github.com/propeller-heads/tycho-execution/commit/bbd732d15a4405fc358eedbb3cbb3b98ec3f61b5))
|
||||
|
||||
## [0.123.0](https://github.com/propeller-heads/tycho-execution/compare/0.122.0...0.123.0) (2025-09-02)
|
||||
|
||||
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -4634,9 +4634,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tycho-common"
|
||||
version = "0.82.0"
|
||||
version = "0.88.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "096c87ebe011785fcd7ed59ec501ac12b465a64fbd2914b8c0c57125c253682b"
|
||||
checksum = "ac0c309443e15797f3b713f10b8f00e79067abe91ea7c58421e4ce345b022e0d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -4659,7 +4659,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tycho-execution"
|
||||
version = "0.123.0"
|
||||
version = "0.128.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"async-trait",
|
||||
|
||||
16
Cargo.toml
16
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tycho-execution"
|
||||
version = "0.123.0"
|
||||
version = "0.128.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"
|
||||
@@ -11,12 +11,12 @@ license = "MIT"
|
||||
categories = ["finance", "cryptography::cryptocurrencies"]
|
||||
readme = "README.md"
|
||||
exclude = [
|
||||
"foundry/*",
|
||||
"foundry",
|
||||
"tests/*",
|
||||
"tests/common",
|
||||
".github/*",
|
||||
".gitmodules",
|
||||
"foundry/*",
|
||||
"foundry",
|
||||
"tests/*",
|
||||
"tests/common",
|
||||
".github/*",
|
||||
".gitmodules",
|
||||
]
|
||||
|
||||
[[bin]]
|
||||
@@ -37,7 +37,7 @@ tokio = { version = "1.38.0", features = ["full"] }
|
||||
chrono = "0.4.39"
|
||||
clap = { version = "4.5.3", features = ["derive"] }
|
||||
once_cell = "1.20.2"
|
||||
tycho-common = "0.82.0"
|
||||
tycho-common = "0.88.0"
|
||||
alloy = { version = "1.0.6", features = ["providers", "rpc-types-eth", "eip712", "signer-local", "node-bindings"], optional = true }
|
||||
async-trait = { version = "0.1.88", optional = true }
|
||||
|
||||
|
||||
1
foundry/test/.gitignore
vendored
Normal file
1
foundry/test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.runtime.json
|
||||
@@ -136,7 +136,6 @@ contract BalancerV2ExecutorTest is Constants, TestUtils {
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
vm.skip(true);
|
||||
exportRuntimeBytecode(address(balancerV2Exposed), "BalancerV2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ contract BalancerV3ExecutorTest is Constants, TestUtils {
|
||||
assertGt(balanceAfter, balanceBefore);
|
||||
assertEq(balanceAfter - balanceBefore, amountOut);
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(balancerV3Exposed), "BalancerV3");
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
|
||||
@@ -46,7 +46,7 @@ contract CurveExecutorExposed is CurveExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
contract CurveExecutorTest is Test, Constants {
|
||||
contract CurveExecutorTest is Test, TestUtils, Constants {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
CurveExecutorExposed curveExecutorExposed;
|
||||
@@ -393,6 +393,10 @@ contract CurveExecutorTest is Test, Constants {
|
||||
metaRegistry.get_coin_indices(pool, tokenIn, tokenOut);
|
||||
return (coinInIndex, coinOutIndex);
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(curveExecutorExposed), "Curve");
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
|
||||
@@ -202,6 +202,10 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
function testMultiHopSwapIntegration() public setUpFork(22082754) {
|
||||
multiHopSwap(loadCallDataFromFile("test_ekubo_encode_swap_multi"));
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(executor), "Ekubo");
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
|
||||
@@ -41,7 +41,7 @@ contract HashflowUtils is Test {
|
||||
}
|
||||
}
|
||||
|
||||
contract HashflowExecutorECR20Test is Constants, HashflowUtils {
|
||||
contract HashflowExecutorECR20Test is Constants, TestUtils, HashflowUtils {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
HashflowExecutorExposed executor;
|
||||
|
||||
@@ -126,6 +126,10 @@ contract MaverickV2ExecutorTest is TestUtils, Constants {
|
||||
assertGt(balanceAfter, balanceBefore);
|
||||
assertEq(balanceAfter - balanceBefore, amountOut);
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(maverickV2Exposed), "MaverickV2");
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
|
||||
@@ -263,4 +263,8 @@ contract UniswapV2ExecutorTest is Constants, Permit2TestHelper, TestUtils {
|
||||
|
||||
assertEq(IERC20(BASE_MAG7).balanceOf(BOB), 1379830606);
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(uniswapV2Exposed), "UniswapV2");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,12 @@ contract UniswapV3ExecutorExposed is UniswapV3Executor {
|
||||
}
|
||||
}
|
||||
|
||||
contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
|
||||
contract UniswapV3ExecutorTest is
|
||||
Test,
|
||||
TestUtils,
|
||||
Constants,
|
||||
Permit2TestHelper
|
||||
{
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
UniswapV3ExecutorExposed uniswapV3Exposed;
|
||||
@@ -210,6 +215,10 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
|
||||
transferType
|
||||
);
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(uniswapV3Exposed), "UniswapV3");
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
|
||||
@@ -261,6 +261,10 @@ contract UniswapV4ExecutorTest is Constants, TestUtils {
|
||||
);
|
||||
assertTrue(IERC20(WETH_ADDR).balanceOf(ALICE) == amountOut);
|
||||
}
|
||||
|
||||
function testExportContract() public {
|
||||
exportRuntimeBytecode(address(uniswapV4Exposed), "UniswapV4");
|
||||
}
|
||||
}
|
||||
|
||||
contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use std::io::{self, Read};
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read},
|
||||
};
|
||||
|
||||
use alloy::sol_types::SolValue;
|
||||
use clap::{Parser, Subcommand};
|
||||
use tycho_common::{hex_bytes::Bytes, models::Chain};
|
||||
use tycho_execution::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
approvals::permit2::PermitSingle,
|
||||
encoder_builders::{TychoExecutorEncoderBuilder, TychoRouterEncoderBuilder},
|
||||
@@ -83,7 +87,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Commands::TychoRouter => {
|
||||
let mut builder = TychoRouterEncoderBuilder::new().chain(chain);
|
||||
if let Some(config_path) = cli.executors_file_path {
|
||||
builder = builder.executors_file_path(config_path);
|
||||
let executors_addresses = fs::read_to_string(&config_path).map_err(|e| {
|
||||
EncodingError::FatalError(format!(
|
||||
"Error reading executors file from {config_path:?}: {e}",
|
||||
))
|
||||
})?;
|
||||
builder = builder.executors_addresses(executors_addresses);
|
||||
}
|
||||
if let Some(router_address) = cli.router_address {
|
||||
builder = builder.router_address(router_address);
|
||||
|
||||
@@ -20,9 +20,10 @@ use crate::encoding::{
|
||||
pub struct TychoRouterEncoderBuilder {
|
||||
chain: Option<Chain>,
|
||||
user_transfer_type: Option<UserTransferType>,
|
||||
executors_file_path: Option<String>,
|
||||
executors_addresses: Option<String>,
|
||||
router_address: Option<Bytes>,
|
||||
swapper_pk: Option<String>,
|
||||
historical_trade: bool,
|
||||
}
|
||||
|
||||
impl Default for TychoRouterEncoderBuilder {
|
||||
@@ -35,10 +36,11 @@ impl TychoRouterEncoderBuilder {
|
||||
pub fn new() -> Self {
|
||||
TychoRouterEncoderBuilder {
|
||||
chain: None,
|
||||
executors_file_path: None,
|
||||
executors_addresses: None,
|
||||
router_address: None,
|
||||
swapper_pk: None,
|
||||
user_transfer_type: None,
|
||||
historical_trade: false,
|
||||
}
|
||||
}
|
||||
pub fn chain(mut self, chain: Chain) -> Self {
|
||||
@@ -51,10 +53,10 @@ impl TychoRouterEncoderBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `executors_file_path` manually.
|
||||
/// If it's not set, the default path will be used (config/executor_addresses.json)
|
||||
pub fn executors_file_path(mut self, executors_file_path: String) -> Self {
|
||||
self.executors_file_path = Some(executors_file_path);
|
||||
/// Sets the `executors_addresses` manually.
|
||||
/// If it's not set, the default value will be used (contents of config/executor_addresses.json)
|
||||
pub fn executors_addresses(mut self, executors_addresses: String) -> Self {
|
||||
self.executors_addresses = Some(executors_addresses);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -65,6 +67,15 @@ impl TychoRouterEncoderBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `historical_trade` manually to true.
|
||||
/// If set to true, it means that the encoded trade will be used in an historical block (as a
|
||||
/// test) and not in the current one. This is relevant for checking token approvals in some
|
||||
/// protocols (like Balancer v2).
|
||||
pub fn historical_trade(mut self) -> Self {
|
||||
self.historical_trade = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `swapper_pk` for the encoder. This is used to sign permit2 objects. This is only
|
||||
/// needed if you intend to get the full calldata for the transfer. We do not recommend
|
||||
/// using this option, you should sign and create the function calldata entirely on your
|
||||
@@ -96,7 +107,7 @@ impl TychoRouterEncoderBuilder {
|
||||
}
|
||||
|
||||
let swap_encoder_registry =
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?;
|
||||
SwapEncoderRegistry::new(self.executors_addresses.clone(), chain)?;
|
||||
|
||||
let signer = if let Some(pk) = self.swapper_pk {
|
||||
let pk = B256::from_str(&pk).map_err(|_| {
|
||||
@@ -115,6 +126,7 @@ impl TychoRouterEncoderBuilder {
|
||||
tycho_router_address,
|
||||
user_transfer_type,
|
||||
signer,
|
||||
self.historical_trade,
|
||||
)?))
|
||||
} else {
|
||||
Err(EncodingError::FatalError(
|
||||
@@ -128,7 +140,7 @@ impl TychoRouterEncoderBuilder {
|
||||
/// Builder pattern for constructing a `TychoExecutorEncoder` with customizable options.
|
||||
pub struct TychoExecutorEncoderBuilder {
|
||||
chain: Option<Chain>,
|
||||
executors_file_path: Option<String>,
|
||||
executors_addresses: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for TychoExecutorEncoderBuilder {
|
||||
@@ -139,17 +151,17 @@ impl Default for TychoExecutorEncoderBuilder {
|
||||
|
||||
impl TychoExecutorEncoderBuilder {
|
||||
pub fn new() -> Self {
|
||||
TychoExecutorEncoderBuilder { chain: None, executors_file_path: None }
|
||||
TychoExecutorEncoderBuilder { chain: None, executors_addresses: None }
|
||||
}
|
||||
pub fn chain(mut self, chain: Chain) -> Self {
|
||||
self.chain = Some(chain);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `executors_file_path` manually.
|
||||
/// Sets the `executors_addresses` manually.
|
||||
/// If it's not set, the default path will be used (config/executor_addresses.json)
|
||||
pub fn executors_file_path(mut self, executors_file_path: String) -> Self {
|
||||
self.executors_file_path = Some(executors_file_path);
|
||||
pub fn executors_addresses(mut self, executors_addresses: String) -> Self {
|
||||
self.executors_addresses = Some(executors_addresses);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -158,7 +170,7 @@ impl TychoExecutorEncoderBuilder {
|
||||
pub fn build(self) -> Result<Box<dyn TychoEncoder>, EncodingError> {
|
||||
if let Some(chain) = self.chain {
|
||||
let swap_encoder_registry =
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?;
|
||||
SwapEncoderRegistry::new(self.executors_addresses.clone(), chain)?;
|
||||
Ok(Box::new(TychoExecutorEncoder::new(swap_encoder_registry)?))
|
||||
} else {
|
||||
Err(EncodingError::FatalError(
|
||||
|
||||
@@ -58,11 +58,12 @@ use crate::encoding::{
|
||||
/// funds.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `chain_id`: Chain ID
|
||||
/// - `encoded_solution`: The solution already encoded by Tycho.
|
||||
/// - `solution`: The high-level solution including tokens, amounts, and receiver info.
|
||||
/// - `token_in_already_in_router`: Whether the input token is already present in the router.
|
||||
/// - `router_address`: The address of the Tycho Router contract.
|
||||
/// - `user_transfer_type`: The desired transfer method.
|
||||
/// - `native_address`: The address used to represent the native token
|
||||
/// - `signer`: Optional signer for permit2
|
||||
///
|
||||
/// # Returns
|
||||
/// A `Result<Transaction, EncodingError>` that either contains the full transaction data (to,
|
||||
|
||||
@@ -26,12 +26,15 @@ use crate::encoding::{
|
||||
/// * `function_signature`: String, the signature for the swap function in the router contract
|
||||
/// * `router_address`: Address of the router to be used to execute swaps
|
||||
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
|
||||
/// * `historical_trade`: Whether the swap is to be done in the current block or in an historical
|
||||
/// one. This is relevant for checking token approvals in some protocols (like Balancer v2).
|
||||
#[derive(Clone)]
|
||||
pub struct SingleSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
function_signature: String,
|
||||
router_address: Bytes,
|
||||
transfer_optimization: TransferOptimization,
|
||||
historical_trade: bool,
|
||||
}
|
||||
|
||||
impl SingleSwapStrategyEncoder {
|
||||
@@ -40,6 +43,7 @@ impl SingleSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
user_transfer_type: UserTransferType,
|
||||
router_address: Bytes,
|
||||
historical_trade: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
|
||||
"singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
|
||||
@@ -57,6 +61,7 @@ impl SingleSwapStrategyEncoder {
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
historical_trade,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -119,6 +124,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_type: transfer,
|
||||
historical_trade: self.historical_trade,
|
||||
};
|
||||
|
||||
let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
|
||||
@@ -171,6 +177,8 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
|
||||
/// * `sequential_swap_validator`: SequentialSwapValidator, responsible for checking validity of
|
||||
/// sequential swap solutions
|
||||
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
|
||||
/// * `historical_trade`: Whether the swap is to be done in the current block or in an historical
|
||||
/// one. This is relevant for checking token approvals in some protocols (like Balancer v2).
|
||||
#[derive(Clone)]
|
||||
pub struct SequentialSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
@@ -180,6 +188,7 @@ pub struct SequentialSwapStrategyEncoder {
|
||||
wrapped_address: Bytes,
|
||||
sequential_swap_validator: SequentialSwapValidator,
|
||||
transfer_optimization: TransferOptimization,
|
||||
historical_trade: bool,
|
||||
}
|
||||
|
||||
impl SequentialSwapStrategyEncoder {
|
||||
@@ -188,6 +197,7 @@ impl SequentialSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
user_transfer_type: UserTransferType,
|
||||
router_address: Bytes,
|
||||
historical_trade: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
|
||||
"sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
|
||||
@@ -210,6 +220,7 @@ impl SequentialSwapStrategyEncoder {
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
historical_trade,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -279,6 +290,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_type: transfer,
|
||||
historical_trade: self.historical_trade,
|
||||
};
|
||||
|
||||
let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
|
||||
@@ -336,6 +348,8 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
/// solutions
|
||||
/// * `router_address`: Address of the router to be used to execute swaps
|
||||
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
|
||||
/// * `historical_trade`: Whether the swap is to be done in the current block or in an historical
|
||||
/// one. This is relevant for checking token approvals in some protocols (like Balancer v2).
|
||||
#[derive(Clone)]
|
||||
pub struct SplitSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
@@ -345,6 +359,7 @@ pub struct SplitSwapStrategyEncoder {
|
||||
split_swap_validator: SplitSwapValidator,
|
||||
router_address: Bytes,
|
||||
transfer_optimization: TransferOptimization,
|
||||
historical_trade: bool,
|
||||
}
|
||||
|
||||
impl SplitSwapStrategyEncoder {
|
||||
@@ -353,6 +368,7 @@ impl SplitSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
user_transfer_type: UserTransferType,
|
||||
router_address: Bytes,
|
||||
historical_trade: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
|
||||
"splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
|
||||
@@ -374,6 +390,7 @@ impl SplitSwapStrategyEncoder {
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
historical_trade,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -479,6 +496,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_type: transfer,
|
||||
historical_trade: self.historical_trade,
|
||||
};
|
||||
|
||||
let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
|
||||
@@ -535,7 +553,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::{collections::HashMap, fs, str::FromStr};
|
||||
|
||||
use alloy::{hex::encode, primitives::hex};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
@@ -555,9 +573,10 @@ mod tests {
|
||||
}
|
||||
|
||||
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
|
||||
let executors_addresses =
|
||||
fs::read_to_string("config/test_executor_addresses.json").unwrap();
|
||||
let eth_chain = eth_chain();
|
||||
SwapEncoderRegistry::new(Some("config/test_executor_addresses.json".to_string()), eth_chain)
|
||||
.unwrap()
|
||||
SwapEncoderRegistry::new(Some(executors_addresses), eth_chain).unwrap()
|
||||
}
|
||||
|
||||
fn router_address() -> Bytes {
|
||||
@@ -591,6 +610,7 @@ mod tests {
|
||||
swap_encoder_registry,
|
||||
UserTransferType::TransferFromPermit2,
|
||||
router_address(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let solution = Solution {
|
||||
@@ -651,6 +671,7 @@ mod tests {
|
||||
swap_encoder_registry,
|
||||
UserTransferType::None,
|
||||
router_address(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let solution = Solution {
|
||||
@@ -732,6 +753,7 @@ mod tests {
|
||||
swap_encoder_registry,
|
||||
UserTransferType::TransferFrom,
|
||||
router_address(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let solution = Solution {
|
||||
@@ -867,6 +889,7 @@ mod tests {
|
||||
swap_encoder_registry,
|
||||
UserTransferType::TransferFromPermit2,
|
||||
Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1015,6 +1038,7 @@ mod tests {
|
||||
swap_encoder_registry,
|
||||
UserTransferType::TransferFrom,
|
||||
Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, fs};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
@@ -21,13 +21,9 @@ pub struct SwapEncoderRegistry {
|
||||
impl SwapEncoderRegistry {
|
||||
/// Populates the registry with the `SwapEncoders` for the given blockchain by parsing the
|
||||
/// executors' addresses in the file at the given path.
|
||||
pub fn new(executors_file_path: Option<String>, chain: Chain) -> Result<Self, EncodingError> {
|
||||
let config_str = if let Some(ref path) = executors_file_path {
|
||||
fs::read_to_string(path).map_err(|e| {
|
||||
EncodingError::FatalError(format!(
|
||||
"Error reading executors file from {executors_file_path:?}: {e}",
|
||||
))
|
||||
})?
|
||||
pub fn new(executors_addresses: Option<String>, chain: Chain) -> Result<Self, EncodingError> {
|
||||
let config_str = if let Some(addresses) = executors_addresses {
|
||||
addresses
|
||||
} else {
|
||||
DEFAULT_EXECUTORS_JSON.to_string()
|
||||
};
|
||||
|
||||
@@ -280,19 +280,20 @@ impl SwapEncoder for BalancerV2SwapEncoder {
|
||||
) -> Result<Vec<u8>, EncodingError> {
|
||||
let token_approvals_manager = ProtocolApprovalsManager::new()?;
|
||||
let token = bytes_to_address(&swap.token_in)?;
|
||||
let approval_needed: bool;
|
||||
let mut approval_needed: bool = true;
|
||||
|
||||
if let Some(router_address) = &encoding_context.router_address {
|
||||
let tycho_router_address = bytes_to_address(router_address)?;
|
||||
approval_needed = token_approvals_manager.approval_needed(
|
||||
token,
|
||||
tycho_router_address,
|
||||
Address::from_str(&self.vault_address)
|
||||
.map_err(|_| EncodingError::FatalError("Invalid vault address".to_string()))?,
|
||||
)?;
|
||||
} else {
|
||||
approval_needed = true;
|
||||
}
|
||||
if !encoding_context.historical_trade {
|
||||
let tycho_router_address = bytes_to_address(router_address)?;
|
||||
approval_needed = token_approvals_manager.approval_needed(
|
||||
token,
|
||||
tycho_router_address,
|
||||
Address::from_str(&self.vault_address).map_err(|_| {
|
||||
EncodingError::FatalError("Invalid vault address".to_string())
|
||||
})?,
|
||||
)?;
|
||||
}
|
||||
};
|
||||
|
||||
let component_id = AlloyBytes::from_str(&swap.component.id)
|
||||
.map_err(|_| EncodingError::FatalError("Invalid component ID".to_string()))?;
|
||||
@@ -1026,6 +1027,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = UniswapV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -1081,6 +1083,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = UniswapV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -1138,6 +1141,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::None,
|
||||
historical_trade: true,
|
||||
};
|
||||
let encoder = BalancerV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -1207,6 +1211,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
|
||||
@@ -1275,6 +1280,7 @@ mod tests {
|
||||
// Token out is the same as the group token out
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
@@ -1318,6 +1324,7 @@ mod tests {
|
||||
group_token_in: usde_address.clone(),
|
||||
group_token_out: wbtc_address.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
// Setup - First sequence: USDE -> USDT
|
||||
@@ -1448,6 +1455,7 @@ mod tests {
|
||||
exact_out: false,
|
||||
router_address: Some(Bytes::default()),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
let encoder = EkuboSwapEncoder::new(String::default(), Chain::Ethereum, None).unwrap();
|
||||
@@ -1490,6 +1498,7 @@ mod tests {
|
||||
exact_out: false,
|
||||
router_address: Some(Bytes::default()),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
let first_swap = SwapBuilder::new(
|
||||
@@ -1687,6 +1696,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::None,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
@@ -1753,6 +1763,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::None,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
@@ -1820,6 +1831,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::None,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
@@ -1888,6 +1900,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = BalancerV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -1940,6 +1953,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
let encoder = MaverickV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -2033,6 +2047,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
let encoder = BebopSwapEncoder::new(
|
||||
@@ -2107,6 +2122,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
let encoder = HashflowSwapEncoder::new(
|
||||
@@ -2200,6 +2216,7 @@ mod tests {
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_type: TransferType::Transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
|
||||
let encoder = HashflowSwapEncoder::new(
|
||||
|
||||
@@ -54,6 +54,7 @@ impl TychoRouterEncoder {
|
||||
router_address: Bytes,
|
||||
user_transfer_type: UserTransferType,
|
||||
signer: Option<PrivateKeySigner>,
|
||||
historical_trade: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let permit2 = if user_transfer_type == UserTransferType::TransferFromPermit2 {
|
||||
Some(Permit2::new()?)
|
||||
@@ -66,18 +67,21 @@ impl TychoRouterEncoder {
|
||||
swap_encoder_registry.clone(),
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
historical_trade,
|
||||
)?,
|
||||
sequential_swap_strategy: SequentialSwapStrategyEncoder::new(
|
||||
chain,
|
||||
swap_encoder_registry.clone(),
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
historical_trade,
|
||||
)?,
|
||||
split_swap_strategy: SplitSwapStrategyEncoder::new(
|
||||
chain,
|
||||
swap_encoder_registry,
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
historical_trade,
|
||||
)?,
|
||||
router_address,
|
||||
permit2,
|
||||
@@ -333,6 +337,7 @@ impl TychoExecutorEncoder {
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_type: transfer,
|
||||
historical_trade: false,
|
||||
};
|
||||
let mut grouped_protocol_data: Vec<Vec<u8>> = vec![];
|
||||
let mut initial_protocol_data: Vec<u8> = vec![];
|
||||
@@ -402,7 +407,7 @@ impl TychoEncoder for TychoExecutorEncoder {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
use std::{collections::HashMap, fs, str::FromStr};
|
||||
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::models::{protocol::ProtocolComponent, Chain};
|
||||
@@ -486,11 +491,9 @@ mod tests {
|
||||
}
|
||||
|
||||
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
|
||||
SwapEncoderRegistry::new(
|
||||
Some("config/test_executor_addresses.json".to_string()),
|
||||
eth_chain(),
|
||||
)
|
||||
.unwrap()
|
||||
let executors_addresses =
|
||||
fs::read_to_string("config/test_executor_addresses.json").unwrap();
|
||||
SwapEncoderRegistry::new(Some(executors_addresses), eth_chain()).unwrap()
|
||||
}
|
||||
|
||||
fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> TychoRouterEncoder {
|
||||
@@ -500,6 +503,7 @@ mod tests {
|
||||
router_address(),
|
||||
user_transfer_type,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -273,6 +273,8 @@ impl PartialEq for PermitDetails {
|
||||
/// * `group_token_in`: Token to be used as the input for the group swap.
|
||||
/// * `group_token_out`: Token to be used as the output for the group swap.
|
||||
/// * `transfer`: Type of transfer to be performed. See `TransferType` for more details.
|
||||
/// * `historical_trade`: Whether the swap is to be done in the current block or in an historical
|
||||
/// one. This is relevant for checking token approvals in some protocols (like Balancer v2).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EncodingContext {
|
||||
pub receiver: Bytes,
|
||||
@@ -281,6 +283,7 @@ pub struct EncodingContext {
|
||||
pub group_token_in: Bytes,
|
||||
pub group_token_out: Bytes,
|
||||
pub transfer_type: TransferType,
|
||||
pub historical_trade: bool,
|
||||
}
|
||||
|
||||
/// Represents the type of transfer to be performed into the pool.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(dead_code)]
|
||||
pub mod encoding;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::{fs, str::FromStr};
|
||||
|
||||
use alloy::{
|
||||
primitives::{B256, U256},
|
||||
@@ -71,10 +71,11 @@ pub fn get_signer() -> PrivateKeySigner {
|
||||
}
|
||||
|
||||
pub fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> Box<dyn TychoEncoder> {
|
||||
let executors_addresses = fs::read_to_string("config/test_executor_addresses.json").unwrap();
|
||||
TychoRouterEncoderBuilder::new()
|
||||
.chain(Chain::Ethereum)
|
||||
.user_transfer_type(user_transfer_type)
|
||||
.executors_file_path("config/test_executor_addresses.json".to_string())
|
||||
.executors_addresses(executors_addresses)
|
||||
.router_address(router_address())
|
||||
.build()
|
||||
.expect("Failed to build encoder")
|
||||
|
||||
Reference in New Issue
Block a user