Merge branch 'refs/heads/main' into hooks/dc/ENG-4624-pass-hook-data
# Conflicts: # foundry/test/assets/calldata.txt Took 9 minutes
This commit is contained in:
133
CHANGELOG.md
133
CHANGELOG.md
@@ -1,3 +1,136 @@
|
||||
## [0.112.0](https://github.com/propeller-heads/tycho-execution/compare/0.111.0...0.112.0) (2025-07-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add protocol state to Swap object ([c217702](https://github.com/propeller-heads/tycho-execution/commit/c21770256045b3fdaddb889effa09b839f59755e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Replace smart pointer with regular pointer ([95c5124](https://github.com/propeller-heads/tycho-execution/commit/95c51247f73516f387e2169e63a22311b4343b8d))
|
||||
|
||||
## [0.111.0](https://github.com/propeller-heads/tycho-execution/compare/0.110.0...0.111.0) (2025-07-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Uniswap X deployment script ([efa6fae](https://github.com/propeller-heads/tycho-execution/commit/efa6fae0e8f3884849dc7ec51f0e32483b483836))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Read Tycho Router addresses from file ([d2d8b29](https://github.com/propeller-heads/tycho-execution/commit/d2d8b290560a435e4b1cac1e3293fe8fd6878091))
|
||||
|
||||
## [0.110.0](https://github.com/propeller-heads/tycho-execution/compare/0.109.0...0.110.0) (2025-07-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* UniswapX encoding example ([2d4b0b9](https://github.com/propeller-heads/tycho-execution/commit/2d4b0b995b903e42c8b48f499cf406c8d5e46254))
|
||||
|
||||
## [0.109.0](https://github.com/propeller-heads/tycho-execution/compare/0.108.0...0.109.0) (2025-07-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add testExecuteIntegration ([4ba5919](https://github.com/propeller-heads/tycho-execution/commit/4ba59192fcfe60d6d4f95b9540a2a7d4da202952))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* chain id call after main update ([e775238](https://github.com/propeller-heads/tycho-execution/commit/e775238b0816e71d383193bfdb0cbbfe64e37b7a))
|
||||
* Replicate a real Uniswap X order in integration test ([96d0bf4](https://github.com/propeller-heads/tycho-execution/commit/96d0bf4ba545e4ae369c292aec6b56a32cf77db0))
|
||||
|
||||
## [0.108.0](https://github.com/propeller-heads/tycho-execution/compare/0.107.0...0.108.0) (2025-07-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Upgrade tycho-common ([8458b46](https://github.com/propeller-heads/tycho-execution/commit/8458b4628e351daaf4d4a639afc5099903107ff6))
|
||||
* Upgrade tycho-common ([effc25c](https://github.com/propeller-heads/tycho-execution/commit/effc25c2d723107a56260190d6f7b5c7b8c42e4e))
|
||||
* Upgrade tycho-common ([cb6042e](https://github.com/propeller-heads/tycho-execution/commit/cb6042ea7974faee93c1a87ca84fc6677c999548))
|
||||
* Use Chain from tycho-core and remove current implementation ([2c25b5a](https://github.com/propeller-heads/tycho-execution/commit/2c25b5a18f06beb61d93530660d9530bf0c46b36))
|
||||
|
||||
## [0.107.0](https://github.com/propeller-heads/tycho-execution/compare/0.106.0...0.107.0) (2025-07-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* (WIP) Handle approvals in UniswapXFiller ([e243667](https://github.com/propeller-heads/tycho-execution/commit/e243667f9ee8ec8e79fc5196da6960b19cd120b7))
|
||||
* Handle native ETH outputs ([3752c15](https://github.com/propeller-heads/tycho-execution/commit/3752c155ea89d4d2d91be98f2c5fa79264a45999))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* silence slither on native address setting ([1b6a24f](https://github.com/propeller-heads/tycho-execution/commit/1b6a24fd75b91de7570f110e87d81ed081639bb7))
|
||||
* use encodePacked to encode if approval needed ([9e2f228](https://github.com/propeller-heads/tycho-execution/commit/9e2f228a470d163b71cac22a0f95116716490772))
|
||||
|
||||
## [0.106.0](https://github.com/propeller-heads/tycho-execution/compare/0.105.0...0.106.0) (2025-07-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* UniswapXFiller callback - naive implementation ([a114dfc](https://github.com/propeller-heads/tycho-execution/commit/a114dfc9da52a6393c55b00ea5b57143c8106429))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disable slither warnings ([a2d123a](https://github.com/propeller-heads/tycho-execution/commit/a2d123a26328f71afa6e8c163096d29bfef6957b))
|
||||
|
||||
## [0.105.0](https://github.com/propeller-heads/tycho-execution/compare/0.104.0...0.105.0) (2025-07-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Upgrade scripts to submit to Safe wallet ([2733bb0](https://github.com/propeller-heads/tycho-execution/commit/2733bb00724fb2df8d6f8151df02826807289c9a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Simplify nonceOffset logic ([ba60e4b](https://github.com/propeller-heads/tycho-execution/commit/ba60e4bb73741c17c44e1cfcdea3fd599ae027eb))
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Deploy Balancer V3 executor ([266e30d](https://github.com/propeller-heads/tycho-execution/commit/266e30d1aa49f3ad04dddeb6a3bb047ed4db90e9))
|
||||
|
||||
## [0.102.0](https://github.com/propeller-heads/tycho-execution/compare/0.101.5...0.102.0) (2025-07-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Deploy EkuboExecutor with MEV-resist ([3fe9906](https://github.com/propeller-heads/tycho-execution/commit/3fe9906e5babb4a31af45c1d48fda3a096802fe8))
|
||||
|
||||
## [0.101.5](https://github.com/propeller-heads/tycho-execution/compare/0.101.4...0.101.5) (2025-06-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use native token curve address and not regular zero address ([0f679d6](https://github.com/propeller-heads/tycho-execution/commit/0f679d6e0663aba881babb09319815723cdf68e5))
|
||||
|
||||
## [0.101.4](https://github.com/propeller-heads/tycho-execution/compare/0.101.3...0.101.4) (2025-06-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix wrong log in CurveEncoder ([312269d](https://github.com/propeller-heads/tycho-execution/commit/312269dabba5b0d8f303f9aba47d9456e5cd5db7))
|
||||
|
||||
## [0.101.3](https://github.com/propeller-heads/tycho-execution/compare/0.101.2...0.101.3) (2025-06-23)
|
||||
|
||||
|
||||
|
||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -4634,15 +4634,16 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tycho-common"
|
||||
version = "0.70.7"
|
||||
version = "0.78.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4da82c4239c76011b8edc19b5d2a2db989c9ff1f74ae6a3dea21486e9b53234"
|
||||
checksum = "2af4c7e2c8e194a3e9dfc5911ff0ec273b7dd52acb71dfdcf21351dd78a49576"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"hex",
|
||||
"num-bigint",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4658,7 +4659,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tycho-execution"
|
||||
version = "0.101.3"
|
||||
version = "0.112.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"chrono",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tycho-execution"
|
||||
version = "0.101.3"
|
||||
version = "0.112.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"
|
||||
@@ -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.66.4"
|
||||
tycho-common = ">0.78.1"
|
||||
|
||||
alloy = { version = "1.0.6", features = ["providers", "rpc-types-eth", "eip712", "signer-local", "node-bindings"], optional = true }
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
"pancakeswap_v3": "0x9D32e9F569B22Ae8d8C6f788037C1CD53632A059",
|
||||
"uniswap_v4": "0xD11496EAb53A9521f0bC1e5c1098Ecb467103Ad9",
|
||||
"vm:balancer_v2": "0xB5b8dc3F0a1Be99685a0DEd015Af93bFBB55C411",
|
||||
"ekubo_v2": "0xcCF8e1E39e9ddfa88282fA6a7B31eBFB41a1ED7B",
|
||||
"ekubo_v2": "0x263DD7AD20983b5E0392bf1F09C4493500EDb333",
|
||||
"vm:curve": "0x879F3008D96EBea0fc584aD684c7Df31777F3165",
|
||||
"vm:maverick_v2": "0xF35e3F5F205769B41508A18787b62A21bC80200B",
|
||||
"vm:balancer_v3": "0x0000000000000000000000000000000000000000"
|
||||
"vm:balancer_v3": "0xec5cE4bF6FbcB7bB0148652c92a4AEC8c1d474Ec"
|
||||
},
|
||||
"base": {
|
||||
"uniswap_v2": "0xF744EBfaA580cF3fFc25aD046E92BD8B770a0700",
|
||||
|
||||
@@ -43,6 +43,7 @@ fn main() {
|
||||
// the amount or the total remaining balance.
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// Then we create a solution object with the previous swap
|
||||
@@ -96,6 +97,7 @@ fn main() {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_wbtc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -109,6 +111,7 @@ fn main() {
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -120,6 +123,7 @@ fn main() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -131,6 +135,7 @@ fn main() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let mut complex_solution = solution.clone();
|
||||
complex_solution.swaps = vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc];
|
||||
|
||||
15
examples/uniswapx-encoding-example/README.md
Normal file
15
examples/uniswapx-encoding-example/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# UniswapX Encoding Example
|
||||
|
||||
This guide enables you to:
|
||||
|
||||
1. Create a Solution object
|
||||
2. Create callback data for executing a UniswapX Order
|
||||
|
||||
Note: This guide only encodes the callback data for you. You will still have to encode the call to the
|
||||
`execute` method of the filler, which also includes the encoded UniswapX order.
|
||||
|
||||
## How to run
|
||||
|
||||
```bash
|
||||
cargo run --release --example uniswapx-encoding-example
|
||||
```
|
||||
172
examples/uniswapx-encoding-example/main.rs
Normal file
172
examples/uniswapx-encoding-example/main.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use alloy::{
|
||||
hex::encode,
|
||||
primitives::{Address, Keccak256},
|
||||
sol_types::SolValue,
|
||||
};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
||||
use tycho_execution::encoding::{
|
||||
evm::{
|
||||
approvals::protocol_approvals_manager::ProtocolApprovalsManager,
|
||||
encoder_builders::TychoRouterEncoderBuilder,
|
||||
utils::{biguint_to_u256, bytes_to_address},
|
||||
},
|
||||
models::{Solution, Swap, UserTransferType},
|
||||
};
|
||||
|
||||
/// Encodes the input data for a function call to the given function selector.
|
||||
pub fn encode_input(selector: &str, mut encoded_args: Vec<u8>) -> Vec<u8> {
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(selector.as_bytes());
|
||||
let selector_bytes = &hasher.finalize()[..4];
|
||||
let mut call_data = selector_bytes.to_vec();
|
||||
// Remove extra prefix if present (32 bytes for dynamic data)
|
||||
// Alloy encoding is including a prefix for dynamic data indicating the offset or length
|
||||
// but at this point we don't want that
|
||||
if encoded_args.len() > 32 &&
|
||||
encoded_args[..32] ==
|
||||
[0u8; 31]
|
||||
.into_iter()
|
||||
.chain([32].to_vec())
|
||||
.collect::<Vec<u8>>()
|
||||
{
|
||||
encoded_args = encoded_args[32..].to_vec();
|
||||
}
|
||||
call_data.extend(encoded_args);
|
||||
call_data
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let router_address = Bytes::from_str("0xfD0b31d2E955fA55e3fa641Fe90e08b677188d35")
|
||||
.expect("Failed to create router address");
|
||||
|
||||
// Initialize the encoder
|
||||
let encoder = TychoRouterEncoderBuilder::new()
|
||||
.chain(tycho_common::models::Chain::Ethereum)
|
||||
.user_transfer_type(UserTransferType::TransferFrom)
|
||||
.router_address(router_address.clone())
|
||||
.build()
|
||||
.expect("Failed to build encoder");
|
||||
|
||||
// Set up UniswapX-related variables
|
||||
let filler = Bytes::from_str("0x6D9da78B6A5BEdcA287AA5d49613bA36b90c15C4").unwrap();
|
||||
let usx_reactor = Address::from_str("0x00000011F84B9aa48e5f8aA8B9897600006289Be").unwrap();
|
||||
|
||||
// ------------------- Encode a sequential swap -------------------
|
||||
// Prepare data to encode. We will encode a sequential swap from DAI to USDT though USDC using
|
||||
// USV3 pools
|
||||
//
|
||||
// DAI ───(USV3)──> USDC ───(USV2)──> USDT
|
||||
//
|
||||
// First we need to create swap objects
|
||||
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let usdt = Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap();
|
||||
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168".to_string(),
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
static_attributes: {
|
||||
let mut attrs = HashMap::new();
|
||||
attrs
|
||||
.insert("fee".to_string(), Bytes::from(BigInt::from(100).to_signed_bytes_be()));
|
||||
attrs
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_usdc_usdt = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0x3416cF6C708Da44DB2624D63ea0AAef7113527C6".to_string(),
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
static_attributes: {
|
||||
let mut attrs = HashMap::new();
|
||||
attrs
|
||||
.insert("fee".to_string(), Bytes::from(BigInt::from(100).to_signed_bytes_be()));
|
||||
attrs
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: usdt.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// Then we create a solution object with the previous swap
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
given_token: dai.clone(),
|
||||
given_amount: BigUint::from_str("2_000_000000000000000000").unwrap(),
|
||||
checked_token: usdt.clone(),
|
||||
checked_amount: BigUint::from_str("1_990_000000").unwrap(),
|
||||
sender: filler.clone(),
|
||||
receiver: filler.clone(),
|
||||
swaps: vec![swap_dai_usdc, swap_usdc_usdt],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Encode the solution using appropriate safety checks
|
||||
let encoded_solution = encoder
|
||||
.encode_solutions(vec![solution.clone()])
|
||||
.unwrap()[0]
|
||||
.clone();
|
||||
|
||||
let given_amount = biguint_to_u256(&solution.given_amount);
|
||||
let min_amount_out = biguint_to_u256(&solution.checked_amount);
|
||||
let given_token = bytes_to_address(&solution.given_token).unwrap();
|
||||
let checked_token = bytes_to_address(&solution.checked_token).unwrap();
|
||||
let receiver = bytes_to_address(&solution.receiver).unwrap();
|
||||
|
||||
let method_calldata = (
|
||||
given_amount,
|
||||
given_token,
|
||||
checked_token,
|
||||
min_amount_out,
|
||||
false, // wrap
|
||||
false, // unwrap
|
||||
receiver,
|
||||
true, // transferFrom permitted
|
||||
encoded_solution.swaps,
|
||||
)
|
||||
.abi_encode();
|
||||
|
||||
let tycho_calldata = encode_input(&encoded_solution.function_signature, method_calldata);
|
||||
|
||||
// Uniswap X specific part (check necessary approvals)
|
||||
let filler_address = bytes_to_address(&filler).unwrap();
|
||||
let token_approvals_manager = ProtocolApprovalsManager::new().unwrap();
|
||||
|
||||
let token_in_approval_needed = token_approvals_manager
|
||||
.approval_needed(
|
||||
bytes_to_address(&dai).unwrap(),
|
||||
filler_address,
|
||||
bytes_to_address(&router_address).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let token_out_approval_needed = token_approvals_manager
|
||||
.approval_needed(bytes_to_address(&usdc).unwrap(), filler_address, usx_reactor)
|
||||
.unwrap();
|
||||
|
||||
let full_calldata =
|
||||
(token_in_approval_needed, token_out_approval_needed, tycho_calldata).abi_encode_packed();
|
||||
|
||||
let hex_calldata = encode(&full_calldata);
|
||||
|
||||
println!(" ====== Simple swap DAI -> USDT ======");
|
||||
println!(
|
||||
"The following callback data should be sent to the filler contract, along with the \
|
||||
encoded order and signature: {hex_calldata:?}"
|
||||
);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
tenderly: {
|
||||
project: "project",
|
||||
project: "tycho",
|
||||
username: "tvinagre",
|
||||
privateVerification: false,
|
||||
},
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import {IFlashAccountant} from "./IFlashAccountant.sol";
|
||||
import {EkuboPoolKey} from "../types/poolKey.sol";
|
||||
import {PoolKey} from "../types/poolKey.sol";
|
||||
import {SqrtRatio} from "../types/sqrtRatio.sol";
|
||||
|
||||
interface ICore is IFlashAccountant {
|
||||
function swap_611415377(
|
||||
EkuboPoolKey memory poolKey,
|
||||
PoolKey memory poolKey,
|
||||
int128 amount,
|
||||
bool isToken1,
|
||||
SqrtRatio sqrtRatioLimit,
|
||||
uint256 skipAhead
|
||||
) external payable returns (int128 delta0, int128 delta1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,17 @@ interface IPayer {
|
||||
}
|
||||
|
||||
interface IFlashAccountant {
|
||||
// Forward the lock from the current locker to the given address
|
||||
// Any additional calldata is also passed through to the forwardee, with no additional encoding
|
||||
// In addition, any data returned from IForwardee#forwarded is also returned from this function exactly as is, i.e. with no additional encoding or decoding
|
||||
// Reverts are also bubbled up
|
||||
function forward(address to) external;
|
||||
|
||||
// Withdraws a token amount from the accountant to the given recipient.
|
||||
// The contract must be locked, as it tracks the withdrawn amount against the current locker's delta.
|
||||
function withdraw(address token, address recipient, uint128 amount) external;
|
||||
function withdraw(
|
||||
address token,
|
||||
address recipient,
|
||||
uint128 amount
|
||||
) external;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
using {extension} for Config global;
|
||||
|
||||
// address (20 bytes) | fee (8 bytes) | tickSpacing (4 bytes)
|
||||
type Config is bytes32;
|
||||
|
||||
// Each pool has its own state associated with this key
|
||||
struct EkuboPoolKey {
|
||||
struct PoolKey {
|
||||
address token0;
|
||||
address token1;
|
||||
Config config;
|
||||
}
|
||||
|
||||
function extension(Config config) pure returns (address e) {
|
||||
// slither-disable-next-line assembly
|
||||
assembly ("memory-safe") {
|
||||
e := shr(96, config)
|
||||
}
|
||||
}
|
||||
|
||||
3793
foundry/package-lock.json
generated
3793
foundry/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nomicfoundation/hardhat-foundry": "^1.1.3",
|
||||
"ethers": "^5.0.0",
|
||||
"@safe-global/api-kit": "^1.1.0",
|
||||
"@safe-global/protocol-kit": "^1.0.1",
|
||||
"ethers": "^5.8.0",
|
||||
"prompt-sync": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,4 +53,36 @@ For each of the following, you must select one of `tenderly_ethereum`, `tenderly
|
||||
|
||||
1. If you set a new executor for the same protocol, you need to remove the old one.
|
||||
2. Run: `npx hardhat run scripts/remove-executor.js --network NETWORK`
|
||||
3. There will be a prompt for you to insert the executor address you want to remove.
|
||||
3. There will be a prompt for you to insert the executor address you want to remove.
|
||||
|
||||
### Revoke roles
|
||||
|
||||
1. If you wish to revoke a role for a certain address, run: `npx hardhat run scripts/revoke-role.js --network NETWORK`
|
||||
2. There will be a prompt for you to insert the role hash and the address you want to revoke it for.
|
||||
|
||||
### Safe wallet
|
||||
|
||||
1. If the wallet that has the role, is a Gnosis Safe, you need to set the `SAFE_ADDRESS` env var.
|
||||
2. The scripts deploy-executors, remove-executor, set-roles and revoke-role all support this.
|
||||
1. If `SAFE_ADDRESS` is set, then it will propose a transaction to the safe wallet and later on it needs to be
|
||||
approved in their UI to execute on chain.
|
||||
2. If it's not set, it will submit the transaction directly to the chain.
|
||||
|
||||
## Deploy Uniswap X filler
|
||||
|
||||
The current script deploys an Uniswap X filler and verifies it in the corresponding blockchain explorer.
|
||||
|
||||
Make sure to run `unset HISTFILE` in your terminal before setting the private key. This will prevent the private key
|
||||
from being stored in the shell history.
|
||||
|
||||
1. Set the following environment variables:
|
||||
|
||||
```
|
||||
export RPC_URL=<chain-rpc-url>
|
||||
export PRIVATE_KEY=<deploy-wallet-private-key>
|
||||
export BLOCKCHAIN_EXPLORER_API_KEY=<blockchain-explorer-api-key>
|
||||
```
|
||||
|
||||
2. Confirm that the variables `tychoRouter`, `uniswapXReactor` and `nativeToken` are correctly set in the script. Make
|
||||
sure that the Uniswap X Reactor address matches the reactor you are targeting.
|
||||
3. Run `npx hardhat run scripts/deploy-uniswap-x-filler.js --network NETWORK`.
|
||||
|
||||
@@ -57,10 +57,11 @@ const executors_to_deploy = {
|
||||
},
|
||||
// Args: Permit2
|
||||
{exchange: "BalancerV2Executor", args: ["0x000000000022D473030F116dDEE9F6B43aC78BA3"]},
|
||||
// Args: Ekubo core contract, Permit2
|
||||
// Args: Ekubo core contract, mev resist, Permit2
|
||||
{
|
||||
exchange: "EkuboExecutor", args: [
|
||||
"0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444",
|
||||
"0x553a2EFc570c9e104942cEC6aC1c18118e54C091",
|
||||
"0x000000000022D473030F116dDEE9F6B43aC78BA3"
|
||||
]
|
||||
},
|
||||
@@ -78,6 +79,8 @@ const executors_to_deploy = {
|
||||
"0x000000000022D473030F116dDEE9F6B43aC78BA3"
|
||||
]
|
||||
},
|
||||
// Args: Permit2
|
||||
{exchange: "BalancerV3Executor", args: ["0x000000000022D473030F116dDEE9F6B43aC78BA3"]},
|
||||
],
|
||||
"base": [
|
||||
// Args: Factory, Pool Init Code Hash, Permit2, Fee BPS
|
||||
|
||||
63
foundry/scripts/deploy-uniswap-x-filler.js
Normal file
63
foundry/scripts/deploy-uniswap-x-filler.js
Normal file
@@ -0,0 +1,63 @@
|
||||
require('dotenv').config();
|
||||
const {ethers} = require("hardhat");
|
||||
const hre = require("hardhat");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
async function main() {
|
||||
const network = hre.network.name;
|
||||
let uniswapXReactor;
|
||||
let nativeToken;
|
||||
if (network === "ethereum") {
|
||||
uniswapXReactor = "0x00000011F84B9aa48e5f8aA8B9897600006289Be";
|
||||
nativeToken = "0x0000000000000000000000000000000000000000";
|
||||
} else if (network === "base") {
|
||||
uniswapXReactor = "0x000000001Ec5656dcdB24D90DFa42742738De729";
|
||||
nativeToken = "0x0000000000000000000000000000000000000000";
|
||||
} else if (network === "unichain") {
|
||||
uniswapXReactor = "0x00000006021a6Bce796be7ba509BBBA71e956e37";
|
||||
nativeToken = "0x0000000000000000000000000000000000000000";
|
||||
} else {
|
||||
throw new Error(`Unsupported network: ${network}`);
|
||||
}
|
||||
|
||||
const routerAddressesFilePath = path.join(__dirname, "../../config/router_addresses.json");
|
||||
const tychoRouter = JSON.parse(fs.readFileSync(routerAddressesFilePath, "utf8"))[network];
|
||||
|
||||
console.log(`Deploying Uniswap X filler to ${network} with:`);
|
||||
console.log(`- Tycho router: ${tychoRouter}`);
|
||||
console.log(`- Uniswap X reactor: ${uniswapXReactor}`);
|
||||
console.log(`- Native token: ${nativeToken}`);
|
||||
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log(`Deploying with account: ${deployer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||
|
||||
const UniswapXFiller = await ethers.getContractFactory("UniswapXFiller");
|
||||
const filler = await UniswapXFiller.deploy(tychoRouter, uniswapXReactor, nativeToken);
|
||||
|
||||
await filler.deployed();
|
||||
console.log(`Uniswap X Filler deployed to: ${filler.address}`);
|
||||
|
||||
console.log("Waiting for 1 minute before verifying the contract on the blockchain explorer...");
|
||||
await new Promise(resolve => setTimeout(resolve, 60000));
|
||||
|
||||
// Verify on Etherscan
|
||||
try {
|
||||
await hre.run("verify:verify", {
|
||||
address: filler.address,
|
||||
constructorArguments: [tychoRouter, uniswapXReactor, nativeToken],
|
||||
});
|
||||
console.log(`Uniswap X filler verified successfully on blockchain explorer!`);
|
||||
} catch (error) {
|
||||
console.error(`Error during blockchain explorer verification:`, error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error("Deployment failed:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,16 +1,22 @@
|
||||
require('dotenv').config();
|
||||
const {ethers} = require("hardhat");
|
||||
const hre = require("hardhat");
|
||||
const {proposeOrSendTransaction} = require("./utils");
|
||||
const prompt = require('prompt-sync')();
|
||||
|
||||
async function main() {
|
||||
const network = hre.network.name;
|
||||
const routerAddress = process.env.ROUTER_ADDRESS;
|
||||
console.log(`Removing executors on TychoRouter at ${routerAddress} on ${network}`);
|
||||
const safeAddress = process.env.SAFE_ADDRESS;
|
||||
if (!routerAddress) {
|
||||
throw new Error("Missing ROUTER_ADDRESS");
|
||||
}
|
||||
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log(`Removing executors with account: ${deployer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||
console.log(`Removing executor on TychoRouter at ${routerAddress} on ${network}`);
|
||||
|
||||
const [signer] = await ethers.getSigners();
|
||||
console.log(`Removing executors with account: ${signer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await signer.getBalance())} ETH`);
|
||||
|
||||
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||
const router = TychoRouter.attach(routerAddress);
|
||||
@@ -22,12 +28,15 @@ async function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Remove executor
|
||||
const tx = await router.removeExecutor(executorAddress, {
|
||||
const txData = {
|
||||
to: router.address,
|
||||
data: router.interface.encodeFunctionData("removeExecutor", [executorAddress]),
|
||||
value: "0",
|
||||
gasLimit: 50000
|
||||
});
|
||||
await tx.wait(); // Wait for the transaction to be mined
|
||||
console.log(`Executor removed at transaction: ${tx.hash}`);
|
||||
};
|
||||
|
||||
const txHash = await proposeOrSendTransaction(safeAddress, txData, signer, "removeExecutor");
|
||||
console.log(`TX hash: ${txHash}`);
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
51
foundry/scripts/revoke-role.js
Normal file
51
foundry/scripts/revoke-role.js
Normal file
@@ -0,0 +1,51 @@
|
||||
require('dotenv').config();
|
||||
const {ethers} = require("hardhat");
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const hre = require("hardhat");
|
||||
const {proposeOrSendTransaction} = require("./utils");
|
||||
const prompt = require('prompt-sync')();
|
||||
|
||||
async function main() {
|
||||
const network = hre.network.name;
|
||||
const routerAddress = process.env.ROUTER_ADDRESS;
|
||||
const safeAddress = process.env.SAFE_ADDRESS;
|
||||
if (!routerAddress) {
|
||||
throw new Error("Missing ROUTER_ADDRESS");
|
||||
}
|
||||
|
||||
console.log(`Revoking role on TychoRouter at ${routerAddress} on ${network}`);
|
||||
|
||||
const [signer] = await ethers.getSigners();
|
||||
console.log(`Setting roles with account: ${signer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await signer.getBalance())} ETH`);
|
||||
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||
const router = TychoRouter.attach(routerAddress);
|
||||
|
||||
const roleHash = prompt("Enter role hash to be removed: ");
|
||||
const address = prompt("Enter the address to remove: ");
|
||||
|
||||
|
||||
if (!roleHash || !address) {
|
||||
console.error("Please provide the executorAddress as an argument.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`Revoking ${roleHash} to the following address:`, address);
|
||||
|
||||
const txData = {
|
||||
to: router.address,
|
||||
data: router.interface.encodeFunctionData("revokeRole", [roleHash, address]),
|
||||
value: "0",
|
||||
};
|
||||
|
||||
const txHash = await proposeOrSendTransaction(safeAddress, txData, signer, "revokeRole");
|
||||
console.log(`TX hash: ${txHash}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error("Error setting roles:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,44 +1,50 @@
|
||||
{
|
||||
"ethereum": {
|
||||
"EXECUTOR_SETTER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0x06e580B872a37402764f909FCcAb0Eb5bb38fe23"
|
||||
],
|
||||
"PAUSER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xB279A562C726F9F3011c1945c9c23Fe1FB631B59",
|
||||
"0xAC3649A6DFBBB230632604f2fc43773977ec6E67"
|
||||
],
|
||||
"UNPAUSER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xB279A562C726F9F3011c1945c9c23Fe1FB631B59",
|
||||
"0xAC3649A6DFBBB230632604f2fc43773977ec6E67"
|
||||
],
|
||||
"FUND_RESCUER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xF621770E96bcf1335150faecf77D757faf7ca4A9"
|
||||
]
|
||||
},
|
||||
"base": {
|
||||
"EXECUTOR_SETTER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0x06e580B872a37402764f909FCcAb0Eb5bb38fe23"
|
||||
],
|
||||
"PAUSER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xB279A562C726F9F3011c1945c9c23Fe1FB631B59",
|
||||
"0xAC3649A6DFBBB230632604f2fc43773977ec6E67"
|
||||
],
|
||||
"UNPAUSER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xB279A562C726F9F3011c1945c9c23Fe1FB631B59",
|
||||
"0xAC3649A6DFBBB230632604f2fc43773977ec6E67"
|
||||
],
|
||||
"FUND_RESCUER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xF621770E96bcf1335150faecf77D757faf7ca4A9"
|
||||
]
|
||||
},
|
||||
"unichain": {
|
||||
"EXECUTOR_SETTER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0x06e580B872a37402764f909FCcAb0Eb5bb38fe23"
|
||||
],
|
||||
"PAUSER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xB279A562C726F9F3011c1945c9c23Fe1FB631B59",
|
||||
"0xAC3649A6DFBBB230632604f2fc43773977ec6E67"
|
||||
],
|
||||
"UNPAUSER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xB279A562C726F9F3011c1945c9c23Fe1FB631B59",
|
||||
"0xAC3649A6DFBBB230632604f2fc43773977ec6E67"
|
||||
],
|
||||
"FUND_RESCUER_ROLE": [
|
||||
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||
"0xF621770E96bcf1335150faecf77D757faf7ca4A9"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,25 @@
|
||||
require('dotenv').config();
|
||||
const {ethers} = require("hardhat");
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const hre = require("hardhat");
|
||||
const path = require('path');
|
||||
const {proposeOrSendTransaction} = require("./utils");
|
||||
const prompt = require('prompt-sync')();
|
||||
|
||||
async function main() {
|
||||
const network = hre.network.name;
|
||||
const routerAddress = process.env.ROUTER_ADDRESS;
|
||||
const safeAddress = process.env.SAFE_ADDRESS;
|
||||
if (!routerAddress) {
|
||||
throw new Error("Missing ROUTER_ADDRESS");
|
||||
}
|
||||
|
||||
console.log(`Setting executors on TychoRouter at ${routerAddress} on ${network}`);
|
||||
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log(`Setting executors with account: ${deployer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||
const [signer] = await ethers.getSigners();
|
||||
const balance = await signer.getBalance();
|
||||
|
||||
console.log(`Using signer: ${signer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(balance)} ETH`);
|
||||
|
||||
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||
const router = TychoRouter.attach(routerAddress);
|
||||
@@ -48,13 +55,16 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set executors
|
||||
const executorAddresses = executorsToSet.map(executor => executor.executor);
|
||||
const tx = await router.setExecutors(executorAddresses, {
|
||||
gasLimit: 300000 // should be around 50k per executor
|
||||
});
|
||||
await tx.wait(); // Wait for the transaction to be mined
|
||||
console.log(`Executors set at transaction: ${tx.hash}`);
|
||||
const executorAddresses = executorsToSet.map(({executor}) => executor);
|
||||
const txData = {
|
||||
to: router.address,
|
||||
data: router.interface.encodeFunctionData("setExecutors", [executorAddresses]),
|
||||
value: "0",
|
||||
gasLimit: 300000
|
||||
};
|
||||
|
||||
const txHash = await proposeOrSendTransaction(safeAddress, txData, signer, "setExecutors");
|
||||
console.log(`TX hash: ${txHash}`);
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
@@ -3,15 +3,21 @@ const {ethers} = require("hardhat");
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const hre = require("hardhat");
|
||||
const {proposeOrSendTransaction} = require("./utils");
|
||||
|
||||
async function main() {
|
||||
const network = hre.network.name;
|
||||
const routerAddress = process.env.ROUTER_ADDRESS;
|
||||
const safeAddress = process.env.SAFE_ADDRESS;
|
||||
if (!routerAddress) {
|
||||
throw new Error("Missing ROUTER_ADDRESS");
|
||||
}
|
||||
|
||||
console.log(`Setting roles on TychoRouter at ${routerAddress} on ${network}`);
|
||||
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log(`Setting roles with account: ${deployer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||
const [signer] = await ethers.getSigners();
|
||||
console.log(`Setting roles with account: ${signer.address}`);
|
||||
console.log(`Account balance: ${ethers.utils.formatEther(await signer.getBalance())} ETH`);
|
||||
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||
const router = TychoRouter.attach(routerAddress);
|
||||
|
||||
@@ -20,7 +26,6 @@ async function main() {
|
||||
|
||||
const roles = {
|
||||
EXECUTOR_SETTER_ROLE: "0x6a1dd52dcad5bd732e45b6af4e7344fa284e2d7d4b23b5b09cb55d36b0685c87",
|
||||
FEE_SETTER_ROLE: "0xe6ad9a47fbda1dc18de1eb5eeb7d935e5e81b4748f3cfc61e233e64f88182060",
|
||||
PAUSER_ROLE: "0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a",
|
||||
UNPAUSER_ROLE: "0x427da25fe773164f88948d3e215c94b6554e2ed5e5f203a821c9f2f6131cf75a",
|
||||
FUND_RESCUER_ROLE: "0x912e45d663a6f4cc1d0491d8f046e06c616f40352565ea1cdb86a0e1aaefa41b"
|
||||
@@ -31,9 +36,15 @@ async function main() {
|
||||
const addresses = rolesDict[network][roleName];
|
||||
if (addresses && addresses.length > 0) {
|
||||
console.log(`Granting ${roleName} to the following addresses:`, addresses);
|
||||
const tx = await router.batchGrantRole(roleHash, addresses);
|
||||
await tx.wait(); // Wait for the transaction to be mined
|
||||
console.log(`Role ${roleName} granted at transaction: ${tx.hash}`);
|
||||
|
||||
const txData = {
|
||||
to: router.address,
|
||||
data: router.interface.encodeFunctionData("batchGrantRole", [roleHash, addresses]),
|
||||
value: "0",
|
||||
};
|
||||
|
||||
const txHash = await proposeOrSendTransaction(safeAddress, txData, signer, "batchGrantRole");
|
||||
console.log(`Role ${roleName} granted at TX hash: ${txHash}`);
|
||||
} else {
|
||||
console.log(`No addresses found for role ${roleName}`);
|
||||
}
|
||||
|
||||
66
foundry/scripts/utils.js
Normal file
66
foundry/scripts/utils.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const {ethers} = require("hardhat");
|
||||
const Safe = require('@safe-global/protocol-kit').default;
|
||||
const {EthersAdapter} = require('@safe-global/protocol-kit');
|
||||
const {default: SafeApiKit} = require("@safe-global/api-kit");
|
||||
|
||||
const txServiceUrls = {
|
||||
mainnet: "https://safe-transaction-mainnet.safe.global",
|
||||
base: "https://safe-transaction-base.safe.global",
|
||||
unichain: "https://safe-transaction-unichain.safe.global",
|
||||
};
|
||||
|
||||
const txServiceUrl = txServiceUrls[hre.network.name];
|
||||
|
||||
async function proposeOrSendTransaction(safeAddress, txData, signer, methodName) {
|
||||
if (safeAddress) {
|
||||
return proposeTransaction(safeAddress, txData, signer, methodName);
|
||||
} else {
|
||||
console.log(`Executing the transaction directly`);
|
||||
const tx = await signer.sendTransaction(txData);
|
||||
await tx.wait();
|
||||
return tx.hash;
|
||||
}
|
||||
}
|
||||
|
||||
async function proposeTransaction(safeAddress, txData, signer, methodName) {
|
||||
const signerAddress = await signer.getAddress();
|
||||
console.log(`Proposing transaction to Safe: ${safeAddress} with account: ${signerAddress}`);
|
||||
|
||||
const ethAdapter = new EthersAdapter({
|
||||
ethers,
|
||||
signerOrProvider: signer,
|
||||
});
|
||||
|
||||
const safeService = new SafeApiKit({txServiceUrl, ethAdapter});
|
||||
|
||||
const safeSdk = await Safe.create({
|
||||
ethAdapter,
|
||||
safeAddress,
|
||||
});
|
||||
let next_nonce = await safeService.getNextNonce(safeAddress);
|
||||
const safeTransaction = await safeSdk.createTransaction({
|
||||
safeTransactionData: {
|
||||
...txData,
|
||||
nonce: next_nonce
|
||||
}
|
||||
});
|
||||
const safeTxHash = await safeSdk.getTransactionHash(safeTransaction);
|
||||
const senderSignature = await safeSdk.signTransactionHash(safeTxHash);
|
||||
|
||||
const proposeArgs = {
|
||||
safeAddress,
|
||||
safeTransactionData: safeTransaction.data,
|
||||
safeTxHash,
|
||||
senderAddress: signerAddress,
|
||||
senderSignature: senderSignature.data,
|
||||
origin: `Proposed from hardhat: ${methodName}`,
|
||||
nonce: next_nonce,
|
||||
};
|
||||
|
||||
await safeService.proposeTransaction(proposeArgs);
|
||||
return safeTxHash;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
proposeOrSendTransaction
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
VaultSwapParams
|
||||
} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
|
||||
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
|
||||
import {ICallback} from "../../interfaces/ICallback.sol";
|
||||
import {ICallback} from "@interfaces/ICallback.sol";
|
||||
|
||||
error BalancerV3Executor__InvalidDataLength();
|
||||
error BalancerV3Executor__SenderIsNotVault(address sender);
|
||||
|
||||
@@ -9,8 +9,12 @@ import {ILocker, IPayer} from "@ekubo/interfaces/IFlashAccountant.sol";
|
||||
import {NATIVE_TOKEN_ADDRESS} from "@ekubo/math/constants.sol";
|
||||
import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol";
|
||||
import {LibBytes} from "@solady/utils/LibBytes.sol";
|
||||
import {Config, EkuboPoolKey} from "@ekubo/types/poolKey.sol";
|
||||
import {MAX_SQRT_RATIO, MIN_SQRT_RATIO} from "@ekubo/types/sqrtRatio.sol";
|
||||
import {Config, PoolKey} from "@ekubo/types/poolKey.sol";
|
||||
import {
|
||||
MAX_SQRT_RATIO,
|
||||
MIN_SQRT_RATIO,
|
||||
SqrtRatio
|
||||
} from "@ekubo/types/sqrtRatio.sol";
|
||||
import {RestrictTransferFrom} from "../RestrictTransferFrom.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
|
||||
@@ -21,11 +25,13 @@ contract EkuboExecutor is
|
||||
ICallback,
|
||||
RestrictTransferFrom
|
||||
{
|
||||
error EkuboExecutor__AddressZero();
|
||||
error EkuboExecutor__InvalidDataLength();
|
||||
error EkuboExecutor__CoreOnly();
|
||||
error EkuboExecutor__UnknownCallback();
|
||||
|
||||
ICore immutable core;
|
||||
address immutable mevResist;
|
||||
|
||||
uint256 constant POOL_DATA_OFFSET = 57;
|
||||
uint256 constant HOP_BYTE_LEN = 52;
|
||||
@@ -33,12 +39,19 @@ contract EkuboExecutor is
|
||||
bytes4 constant LOCKED_SELECTOR = 0xb45a3c0e; // locked(uint256)
|
||||
bytes4 constant PAY_CALLBACK_SELECTOR = 0x599d0714; // payCallback(uint256,address)
|
||||
|
||||
uint256 constant SKIP_AHEAD = 0;
|
||||
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
constructor(address _core, address _permit2)
|
||||
constructor(address _core, address _mevResist, address _permit2)
|
||||
RestrictTransferFrom(_permit2)
|
||||
{
|
||||
core = ICore(_core);
|
||||
|
||||
if (_mevResist == address(0)) {
|
||||
revert EkuboExecutor__AddressZero();
|
||||
}
|
||||
mevResist = _mevResist;
|
||||
}
|
||||
|
||||
function swap(uint256 amountIn, bytes calldata data)
|
||||
@@ -141,19 +154,40 @@ contract EkuboExecutor is
|
||||
Config poolConfig =
|
||||
Config.wrap(LibBytes.loadCalldata(swapData, offset + 20));
|
||||
|
||||
(address token0, address token1, bool isToken1) = nextTokenIn
|
||||
> nextTokenOut
|
||||
? (nextTokenOut, nextTokenIn, true)
|
||||
: (nextTokenIn, nextTokenOut, false);
|
||||
(
|
||||
address token0,
|
||||
address token1,
|
||||
bool isToken1,
|
||||
SqrtRatio sqrtRatioLimit
|
||||
) = nextTokenIn > nextTokenOut
|
||||
? (nextTokenOut, nextTokenIn, true, MAX_SQRT_RATIO)
|
||||
: (nextTokenIn, nextTokenOut, false, MIN_SQRT_RATIO);
|
||||
|
||||
// slither-disable-next-line calls-loop
|
||||
(int128 delta0, int128 delta1) = core.swap_611415377(
|
||||
EkuboPoolKey(token0, token1, poolConfig),
|
||||
nextAmountIn,
|
||||
isToken1,
|
||||
isToken1 ? MAX_SQRT_RATIO : MIN_SQRT_RATIO,
|
||||
0
|
||||
);
|
||||
PoolKey memory pk = PoolKey(token0, token1, poolConfig);
|
||||
|
||||
int128 delta0;
|
||||
int128 delta1;
|
||||
|
||||
if (poolConfig.extension() == mevResist) {
|
||||
(delta0, delta1) = abi.decode(
|
||||
_forward(
|
||||
mevResist,
|
||||
abi.encode(
|
||||
pk,
|
||||
nextAmountIn,
|
||||
isToken1,
|
||||
sqrtRatioLimit,
|
||||
SKIP_AHEAD
|
||||
)
|
||||
),
|
||||
(int128, int128)
|
||||
);
|
||||
} else {
|
||||
// slither-disable-next-line calls-loop
|
||||
(delta0, delta1) = core.swap_611415377(
|
||||
pk, nextAmountIn, isToken1, sqrtRatioLimit, SKIP_AHEAD
|
||||
);
|
||||
}
|
||||
|
||||
nextTokenIn = nextTokenOut;
|
||||
nextAmountIn = -(isToken1 ? delta0 : delta1);
|
||||
@@ -166,6 +200,45 @@ contract EkuboExecutor is
|
||||
return nextAmountIn;
|
||||
}
|
||||
|
||||
function _forward(address to, bytes memory data)
|
||||
internal
|
||||
returns (bytes memory result)
|
||||
{
|
||||
address target = address(core);
|
||||
|
||||
// slither-disable-next-line assembly
|
||||
assembly ("memory-safe") {
|
||||
// We will store result where the free memory pointer is now, ...
|
||||
result := mload(0x40)
|
||||
|
||||
// But first use it to store the calldata
|
||||
|
||||
// Selector of forward(address)
|
||||
mstore(result, shl(224, 0x101e8952))
|
||||
mstore(add(result, 4), to)
|
||||
|
||||
// We only copy the data, not the length, because the length is read from the calldata size
|
||||
let len := mload(data)
|
||||
mcopy(add(result, 36), add(data, 32), len)
|
||||
|
||||
// If the call failed, pass through the revert
|
||||
if iszero(call(gas(), target, 0, result, add(36, len), 0, 0)) {
|
||||
returndatacopy(result, 0, returndatasize())
|
||||
revert(result, returndatasize())
|
||||
}
|
||||
|
||||
// Copy the entire return data into the space where the result is pointing
|
||||
mstore(result, returndatasize())
|
||||
returndatacopy(add(result, 32), 0, returndatasize())
|
||||
|
||||
// Update the free memory pointer to be after the end of the data, aligned to the next 32 byte word
|
||||
mstore(
|
||||
0x40,
|
||||
and(add(add(result, add(32, returndatasize())), 31), not(31))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function _pay(address token, uint128 amount, TransferType transferType)
|
||||
internal
|
||||
{
|
||||
|
||||
31
foundry/src/uniswap_x/IReactor.sol
Normal file
31
foundry/src/uniswap_x/IReactor.sol
Normal file
@@ -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;
|
||||
}
|
||||
16
foundry/src/uniswap_x/IReactorCallback.sol
Normal file
16
foundry/src/uniswap_x/IReactorCallback.sol
Normal file
@@ -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;
|
||||
}
|
||||
114
foundry/src/uniswap_x/IStructs.sol
Normal file
114
foundry/src/uniswap_x/IStructs.sol
Normal file
@@ -0,0 +1,114 @@
|
||||
// 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;
|
||||
}
|
||||
164
foundry/src/uniswap_x/UniswapXFiller.sol
Normal file
164
foundry/src/uniswap_x/UniswapXFiller.sol
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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";
|
||||
import {TychoRouter} from "../TychoRouter.sol";
|
||||
|
||||
error UniswapXFiller__AddressZero();
|
||||
error UniswapXFiller__BatchExecutionNotSupported();
|
||||
|
||||
contract UniswapXFiller is AccessControl, IReactorCallback {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
// UniswapX V2DutchOrder Reactor
|
||||
IReactor public immutable reactor;
|
||||
address public immutable tychoRouter;
|
||||
address public immutable nativeAddress;
|
||||
|
||||
// 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,
|
||||
address _reactor,
|
||||
address _native_address
|
||||
) {
|
||||
if (_tychoRouter == address(0)) revert UniswapXFiller__AddressZero();
|
||||
if (_reactor == address(0)) revert UniswapXFiller__AddressZero();
|
||||
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
|
||||
_grantRole(REACTOR_ROLE, address(_reactor));
|
||||
tychoRouter = _tychoRouter;
|
||||
reactor = IReactor(_reactor);
|
||||
|
||||
// slither-disable-next-line missing-zero-check
|
||||
nativeAddress = _native_address;
|
||||
}
|
||||
|
||||
function execute(SignedOrder calldata order, bytes calldata callbackData)
|
||||
external
|
||||
onlyRole(EXECUTOR_ROLE)
|
||||
{
|
||||
reactor.executeWithCallback(order, callbackData);
|
||||
}
|
||||
|
||||
function reactorCallback(
|
||||
ResolvedOrder[] calldata resolvedOrders,
|
||||
bytes calldata callbackData
|
||||
) external onlyRole(REACTOR_ROLE) {
|
||||
require(
|
||||
resolvedOrders.length == 1,
|
||||
UniswapXFiller__BatchExecutionNotSupported()
|
||||
);
|
||||
|
||||
ResolvedOrder memory order = resolvedOrders[0];
|
||||
|
||||
bool tokenInApprovalNeeded = bool(uint8(callbackData[0]) == 1);
|
||||
bool tokenOutApprovalNeeded = bool(uint8(callbackData[1]) == 1);
|
||||
bytes calldata tychoCalldata = bytes(callbackData[2:]);
|
||||
|
||||
// The TychoRouter will take the input tokens from the filler
|
||||
if (tokenInApprovalNeeded) {
|
||||
// Native ETH input is not supported by UniswapX
|
||||
IERC20(order.input.token).forceApprove(
|
||||
tychoRouter, type(uint256).max
|
||||
);
|
||||
}
|
||||
|
||||
// slither-disable-next-line low-level-calls
|
||||
(bool success, bytes memory result) = tychoRouter.call(tychoCalldata);
|
||||
|
||||
if (!success) {
|
||||
revert(
|
||||
string(
|
||||
result.length > 0
|
||||
? result
|
||||
: abi.encodePacked("Execution failed")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (tokenOutApprovalNeeded) {
|
||||
// Multiple outputs are possible when taking fees - but token itself should
|
||||
// not change.
|
||||
OutputToken memory output = order.outputs[0];
|
||||
if (output.token != nativeAddress) {
|
||||
IERC20 token = IERC20(output.token);
|
||||
token.forceApprove(address(reactor), type(uint256).max);
|
||||
} else {
|
||||
// With native ETH - the filler is responsible for transferring back
|
||||
// to the reactor.
|
||||
Address.sendValue(payable(address(reactor)), output.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
// slither-disable-next-line locked-ether
|
||||
receive() external payable {
|
||||
require(msg.sender.code.length != 0);
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@ contract Constants is Test, BaseConstants {
|
||||
|
||||
// Uniswap v3
|
||||
address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8;
|
||||
address DAI_USDT_USV3 = 0x48DA0965ab2d2cbf1C17C09cFB5Cbe67Ad5B1406;
|
||||
address USDC_WETH_USV3 = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640; // 0.05% fee
|
||||
address USDC_WETH_USV3_2 = 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8; // 0.3% fee
|
||||
|
||||
|
||||
@@ -96,7 +96,6 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
||||
}
|
||||
|
||||
vm.startPrank(FUND_RESCUER);
|
||||
|
||||
tychoRouter.withdraw(tokens, FUND_RESCUER);
|
||||
|
||||
// Check balances after withdrawing
|
||||
|
||||
@@ -114,6 +114,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
bytes32 initCodePancakeV3 = PANCAKEV3_POOL_CODE_INIT_HASH;
|
||||
address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90;
|
||||
address ekuboCore = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444;
|
||||
address ekuboMevResist = 0x553a2EFc570c9e104942cEC6aC1c18118e54C091;
|
||||
|
||||
IPoolManager poolManager = IPoolManager(poolManagerAddress);
|
||||
usv2Executor =
|
||||
@@ -125,7 +126,8 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper, TestUtils {
|
||||
factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS
|
||||
);
|
||||
balancerv2Executor = new BalancerV2Executor(PERMIT2_ADDRESS);
|
||||
ekuboExecutor = new EkuboExecutor(ekuboCore, PERMIT2_ADDRESS);
|
||||
ekuboExecutor =
|
||||
new EkuboExecutor(ekuboCore, ekuboMevResist, PERMIT2_ADDRESS);
|
||||
curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS);
|
||||
maverickv2Executor =
|
||||
new MaverickV2Executor(MAVERICK_V2_FACTORY, PERMIT2_ADDRESS);
|
||||
|
||||
@@ -3,33 +3,34 @@ test_single_encoding_strategy_ekubo:5c4b639c000000000000000000000000000000000000
|
||||
test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000
|
||||
test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
|
||||
test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
|
||||
test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006880f5c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fcf00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041c4cbbfa526fdc8da41a6bd03a58365bceecee48a98a0d3748596615854b6a0173244dbe5371ba516092fdd94ff7b57929782bcc25214f473522f97a6d01c10281c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c001a6982508145454ce325ddbe47a25d4ec3d23119330061a80001f400000000
|
||||
test_single_encoding_strategy_usv4_eth_out:30ace1b100000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006880f5c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fcf00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004141834cb6f31447a97a42587522f6bd70c9661678f0fde1350a2d063ec4a6fa547b25d0b81d13b57f02d5f7987490f886b182379c2f24404e9eeba945c95026a11b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c
|
||||
test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f5c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000411d4790c58f29180e53b2043be583c77b00ae13951ac5d2134fe152c41ca8ce1e36489614de5090266bf2ac137f6989fc93ef0df5e33fbbaebb77f88f63054bf91c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
|
||||
test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000068a7452c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3400000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041aa05dfce0bca6785026b114e751b149b0e1ef2649598455bbc013893d1a241c546ccd30a543e261c8bb565c673b9c7fcc28f72325d9d059e7fc4e31a299c895a1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000
|
||||
test_single_encoding_strategy_usv4_eth_out:30ace1b100000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000068a7452c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3400000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041dacc6c754032bb9de8d264c64a46f2b309335b01f837b2f5b8b7b68bfa920a4537220bab168a5b0f3b84469365c91e0013d3950aca01664b993b3c18c291545b1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c0000000000000000000000000000000000000000
|
||||
test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068a7452c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3400000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ca1a9b5e5aef9624ec8e786ff0fb16fc4ed0758a421d99754184430aa95833b223eee4e65bcbf835f5f2ea2bdc2046231f6befb919298bfeb7d0271d0ce27f391c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
|
||||
test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f5c700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fcf00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004171bd5e50eb1f6509d7ea85dcd69039787fc989f5fc98fbbeaa1dcd6e90d5c42608d3b134cad016d9751f51ab69031c83e17525c7b38fbc3a0591a1caa1d9020d1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330061a80001f4
|
||||
test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006880f5c800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004194a2a77939d54575730a79858b5ed5ef2bc53fc4443f0082c2e430e6eaa3019a18f78449bc10dc25a957fe296280b7ebc349784d94da323e553e95807c4db5661b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000
|
||||
test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068a7452c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3400000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000413b7112285f6b953c073324f6563a055155581aacaa2df8bc92cc21619fda72aa25c386899fe9af623d0419095b7b8f4f9af2466e1eebb4d2724dc4e9097d0b111c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000
|
||||
test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068a7452c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3400000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eb48a5bb3d9854bd934b6524f5f568228c9946b22c1f6fd96e3f54ba6d57ce53a9bd5038f43dff33737b1c3f9294336bb9750e8f6c81d3e56b9e087845657c91c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000
|
||||
test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
|
||||
test_single
|
||||
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000064000001001a2260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
|
||||
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
|
||||
6d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006869398600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006841b38e00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041752ca399237fc5086ef89d5f6dabecfb4b43c0753ecfb7020a6a86045db423fd3be9565f79b511fe93f55f76f61b1ac8d786b04051110ca6cbe10bbf69901b871c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
|
||||
test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006880f5c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd100000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041fd9aec9b11fd06d7b9354c7d536af531e4aeb15e0a649b0916d06bb2b60d1858048232393ab20171925e560adeb7d7be93eb3cc66e7568417c4906ebaff388931c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f5c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd100000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041d81384f3eea5db7a09afe5126788555cfef72a084d8b124851f99f4f2a08510959c8fb93042e154563f6bcb95fbed6bfa8f2a3e853b09fe6dd75cb30a6bc1f481b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
|
||||
test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006880f5c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd10000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000414602b0f66daeb285fda8d3e2d68046ca8ae4ee3898923742b84020d3fb4d99227021760217b657f4bca96e64721a5a1c45aec23a04aec8fd3d6b41a1638799af1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000
|
||||
test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006880f5c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd10000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000414602b0f66daeb285fda8d3e2d68046ca8ae4ee3898923742b84020d3fb4d99227021760217b657f4bca96e64721a5a1c45aec23a04aec8fd3d6b41a1638799af1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000
|
||||
test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f5c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd100000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004133513df7889736193c90e980405812c09e90901e4a6aa0c91ce6df1db13812157fadc761eaab59f84c5e135c4fde745836d626ca56390aed4d385538733b00db1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000068a7452d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000416cf5d0fb75c24a379e1988e7ffa76de4e9573ecd42f79a380566f720734698e56cbb6034bd9cf713e6c4a89c4a57f768a360fc83df588fed402be248e2291cc51c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068a7452d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3500000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e3abc1917a9102020e3a8e5f3c9cd8c95088f0f54315938b0fc21d9c74d33ca969bea7ea4cc8889cc403f3940e51b55ee56a5b918e649b5a147d2e47b68411fc1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
|
||||
test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068a7452e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf360000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416619afeffa8f49edde843d038e64e278de064d24586a84d3f6fc592d0e9e263e1f59599118eb7b25a8d4c1362b52728b076997b99ce7fd780b77ddf9ee674bb71c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000
|
||||
test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068a7452e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf360000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416619afeffa8f49edde843d038e64e278de064d24586a84d3f6fc592d0e9e263e1f59599118eb7b25a8d4c1362b52728b076997b99ce7fd780b77ddf9ee674bb71c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000
|
||||
test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068a7452e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf36000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c742469c49e8a03a8285ba3328e786a99b8a792c642fd9e409bc06f3baee04ea6589bdb0bd195122e9af08c8296f9841fff7a32be8b3afd44a586ee65ed684151b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000
|
||||
test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000
|
||||
test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006880f5c600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fce00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004125a37e4e8921f2f34b6264af82e02176c4efa11c3956088cd0dffc923b3406581eb6696baebf6e2b0d2e3b6a6402fea0e314f2ba8d9434b704dc2272a63cf0831b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022800525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c432000000320080f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c000000000000000000000000000000000000000000000000
|
||||
test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000000000000000000000000000000000000068a7452b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3300000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417fa6c0b85fb91e2737e39084c4530258c41c99f16a2796ea7400494d6486496525c653898965dde735537ef4b7ee93a79b5c6f11196a35b79e6d00f0b34811ac1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000
|
||||
test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102
|
||||
test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032
|
||||
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000064000001001a2260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
|
||||
test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000064000001
|
||||
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c
|
||||
test_encode_uniswap_v4_simple_swap:4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec7000064000001
|
||||
test_single_encoding_strategy_maverick:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000040d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000051a4ad4f68d0b91cfd19687c881e50f3a00242828c40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c67cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
|
||||
test_encode_maverick_v2:40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f14cf6d2fe3e1b326114b07d22a6f6bb59e346c671d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01
|
||||
test_encode_uniswap_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb111d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0001
|
||||
test_encode_balancer_v3:7bc3485026ac48b6cf9baf0a377477fff5703af8c71ea051a5f82c67adcf634c36ffe6334793d24c85b2b559bc2d21104c4defdd6efca8a20343361d011d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e
|
||||
test_single_encoding_strategy_balancer_v3:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000097ffedb80d4b2ca6105a07a4d90eb739c45a66600000000000000000000000030881baa943777f92dc934d53d3bfdf33382cab300000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000006503a6a84cd762d9707a21605b548aaab891562aab097ffedb80d4b2ca6105a07a4d90eb739c45a66630881baa943777f92dc934d53d3bfdf33382cab3f028ac624074d6793c36dc8a06ecec0f5a39a71800cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000
|
||||
test_uniswap_v3_balancer_v3:e21dd0d3000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000004a220e6096b25eadb88358cb44068a324825467500000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d200692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed0000006503a6a84cd762d9707a21605b548aaab891562aab2260fac5e5542a773aa44fbcfedf7c193bc2c5994a220e6096b25eadb88358cb44068a3248254675571bea0e99e139cd0b6b7d9352ca872dfe0d72dd01cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000
|
||||
test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f5c900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596fd100000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004133513df7889736193c90e980405812c09e90901e4a6aa0c91ce6df1db13812157fadc761eaab59f84c5e135c4fde745836d626ca56390aed4d385538733b00db1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
|
||||
test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068a7452d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000687fbf3500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004154d32a9cdc3955c1e9ba8767bb29c3a197c7497bf87aba59eb050278f39a999a609510cf1aa2535142e67b07ce9755f926877ff618c5ffaa684c0bcc310fdd7d1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
|
||||
test_sequential_swap_usx:0101e21dd0d300000000000000000000000000000000000000000000006c6b935b8bbd4000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000769cfd80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d9da78b6a5bedca287aa5d49613ba36b90c15c40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470b6b175474e89094c44da98b954eedeac495271d0fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000643ede3eca2a72b3aecc820e955b36f38437d013955777d92f208679db4b9778590fa3cab3ac9e2168010000692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48dac17f958d2ee523a2206206994597c13d831ec70000646d9da78b6a5bedca287aa5d49613ba36b90c15c43416cf6c708da44db2624d63ea0aaef7113527c6010100000000000000000000
|
||||
|
||||
@@ -19,22 +19,29 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
IERC20 USDT = IERC20(USDT_ADDR);
|
||||
|
||||
address constant CORE_ADDRESS = 0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444;
|
||||
address constant MEV_RESIST_ADDRESS =
|
||||
0x553a2EFc570c9e104942cEC6aC1c18118e54C091;
|
||||
|
||||
bytes32 constant ORACLE_CONFIG =
|
||||
0x51d02a5948496a67827242eabc5725531342527c000000000000000000000000;
|
||||
|
||||
function setUp() public {
|
||||
vm.createSelectFork(vm.rpcUrl("mainnet"), 22082754);
|
||||
// 0.01% fee and 0.02% tick spacing
|
||||
bytes32 constant MEV_RESIST_POOL_CONFIG =
|
||||
0x553a2EFc570c9e104942cEC6aC1c18118e54C09100068db8bac710cb000000c8;
|
||||
|
||||
modifier setUpFork(uint256 blockNumber) {
|
||||
vm.createSelectFork(vm.rpcUrl("mainnet"), blockNumber);
|
||||
|
||||
deployCodeTo(
|
||||
"executors/EkuboExecutor.sol",
|
||||
abi.encode(CORE_ADDRESS, PERMIT2_ADDRESS),
|
||||
abi.encode(CORE_ADDRESS, MEV_RESIST_ADDRESS, PERMIT2_ADDRESS),
|
||||
EXECUTOR_ADDRESS
|
||||
);
|
||||
executor = EkuboExecutor(payable(EXECUTOR_ADDRESS));
|
||||
_;
|
||||
}
|
||||
|
||||
function testSingleSwapEth() public {
|
||||
function testSingleSwapEth() public setUpFork(22722989) {
|
||||
uint256 amountIn = 1 ether;
|
||||
|
||||
deal(address(executor), amountIn);
|
||||
@@ -71,7 +78,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
);
|
||||
}
|
||||
|
||||
function testSingleSwapERC20() public {
|
||||
function testSingleSwapERC20() public setUpFork(22722989) {
|
||||
uint256 amountIn = 1_000_000_000;
|
||||
|
||||
deal(USDC_ADDR, address(executor), amountIn);
|
||||
@@ -108,6 +115,43 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
);
|
||||
}
|
||||
|
||||
function testMevResist() public setUpFork(22722989) {
|
||||
uint256 amountIn = 1_000_000_000;
|
||||
|
||||
deal(USDC_ADDR, address(executor), amountIn);
|
||||
|
||||
uint256 usdcBalanceBeforeCore = USDC.balanceOf(CORE_ADDRESS);
|
||||
uint256 usdcBalanceBeforeExecutor = USDC.balanceOf(address(executor));
|
||||
|
||||
uint256 ethBalanceBeforeCore = CORE_ADDRESS.balance;
|
||||
uint256 ethBalanceBeforeExecutor = address(executor).balance;
|
||||
|
||||
bytes memory data = abi.encodePacked(
|
||||
uint8(RestrictTransferFrom.TransferType.Transfer), // transferNeeded (transfer from executor to core)
|
||||
address(executor), // receiver
|
||||
USDC_ADDR, // tokenIn
|
||||
NATIVE_TOKEN_ADDRESS, // tokenOut
|
||||
MEV_RESIST_POOL_CONFIG // config
|
||||
);
|
||||
|
||||
uint256 gasBefore = gasleft();
|
||||
uint256 amountOut = executor.swap(amountIn, data);
|
||||
console.log(gasBefore - gasleft());
|
||||
|
||||
console.log(amountOut);
|
||||
|
||||
assertEq(USDC.balanceOf(CORE_ADDRESS), usdcBalanceBeforeCore + amountIn);
|
||||
assertEq(
|
||||
USDC.balanceOf(address(executor)),
|
||||
usdcBalanceBeforeExecutor - amountIn
|
||||
);
|
||||
|
||||
assertEq(CORE_ADDRESS.balance, ethBalanceBeforeCore - amountOut);
|
||||
assertEq(
|
||||
address(executor).balance, ethBalanceBeforeExecutor + amountOut
|
||||
);
|
||||
}
|
||||
|
||||
// Expects input that encodes the same test case as swap_encoder::tests::ekubo::test_encode_swap_multi
|
||||
function multiHopSwap(bytes memory data) internal {
|
||||
uint256 amountIn = 1 ether;
|
||||
@@ -139,7 +183,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
}
|
||||
|
||||
// Same test case as in swap_encoder::tests::ekubo::test_encode_swap_multi
|
||||
function testMultiHopSwap() public {
|
||||
function testMultiHopSwap() public setUpFork(22082754) {
|
||||
bytes memory data = abi.encodePacked(
|
||||
uint8(RestrictTransferFrom.TransferType.Transfer), // transferNeeded (transfer from executor to core)
|
||||
address(executor), // receiver
|
||||
@@ -155,7 +199,7 @@ contract EkuboExecutorTest is Constants, TestUtils {
|
||||
}
|
||||
|
||||
// Data is generated by test case in swap_encoder::tests::ekubo::test_encode_swap_multi
|
||||
function testMultiHopSwapIntegration() public {
|
||||
function testMultiHopSwapIntegration() public setUpFork(22082754) {
|
||||
multiHopSwap(loadCallDataFromFile("test_ekubo_encode_swap_multi"));
|
||||
}
|
||||
}
|
||||
|
||||
302
foundry/test/uniswap_x/UniswapXFiller.t.sol
Normal file
302
foundry/test/uniswap_x/UniswapXFiller.t.sol
Normal file
@@ -0,0 +1,302 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "@src/uniswap_x/UniswapXFiller.sol";
|
||||
import "../TychoRouterTestSetup.sol";
|
||||
|
||||
contract UniswapXFillerTest is Test, TychoRouterTestSetup {
|
||||
address EXECUTOR = address(0xCe79b081c0c924cb67848723ed3057234d10FC6b);
|
||||
address REACTOR = address(0x00000011F84B9aa48e5f8aA8B9897600006289Be);
|
||||
|
||||
UniswapXFiller filler;
|
||||
address fillerAddr;
|
||||
|
||||
event CallbackVerifierSet(address indexed callbackVerifier);
|
||||
event Withdrawal(
|
||||
address indexed token, uint256 amount, address indexed receiver
|
||||
);
|
||||
|
||||
function getForkBlock() public pure override returns (uint256) {
|
||||
return 22880493;
|
||||
}
|
||||
|
||||
function fillerSetup() public {
|
||||
vm.startPrank(ADMIN);
|
||||
filler = new UniswapXFiller(tychoRouterAddr, REACTOR, address(0));
|
||||
fillerAddr = address(filler);
|
||||
filler.grantRole(keccak256("EXECUTOR_ROLE"), EXECUTOR);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testTychoAddressZeroTychoRouter() public {
|
||||
vm.expectRevert(UniswapXFiller__AddressZero.selector);
|
||||
filler = new UniswapXFiller(address(0), REACTOR, address(0));
|
||||
}
|
||||
|
||||
function testTychoAddressZeroReactor() public {
|
||||
vm.expectRevert(UniswapXFiller__AddressZero.selector);
|
||||
filler = new UniswapXFiller(tychoRouterAddr, address(0), address(0));
|
||||
}
|
||||
|
||||
function testCallback() public {
|
||||
fillerSetup();
|
||||
uint256 amountIn = 10 ** 18;
|
||||
uint256 amountOut = 1847751195973566072891;
|
||||
bool zeroForOne = false;
|
||||
bytes memory protocolData = abi.encodePacked(
|
||||
WETH_ADDR,
|
||||
WETH_DAI_POOL,
|
||||
address(filler),
|
||||
zeroForOne,
|
||||
RestrictTransferFrom.TransferType.TransferFrom
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv2Executor), protocolData);
|
||||
|
||||
bytes memory tychoRouterData = abi.encodeWithSelector(
|
||||
tychoRouter.singleSwap.selector,
|
||||
amountIn,
|
||||
WETH_ADDR,
|
||||
DAI_ADDR,
|
||||
2008817438608734439722,
|
||||
false,
|
||||
false,
|
||||
address(filler),
|
||||
true,
|
||||
swap
|
||||
);
|
||||
|
||||
bytes memory callbackData =
|
||||
abi.encodePacked(true, true, tychoRouterData);
|
||||
|
||||
deal(WETH_ADDR, address(filler), amountIn);
|
||||
|
||||
ResolvedOrder[] memory orders = new ResolvedOrder[](1);
|
||||
OutputToken[] memory outputs = new OutputToken[](1);
|
||||
outputs[0] = OutputToken({
|
||||
token: address(DAI_ADDR),
|
||||
amount: 1847751195973566072891,
|
||||
recipient: BOB
|
||||
});
|
||||
// Irrelevant fields for this test - we only need token output
|
||||
// info for the sake of testing.
|
||||
orders[0] = ResolvedOrder({
|
||||
info: OrderInfo({
|
||||
reactor: address(0),
|
||||
swapper: address(0),
|
||||
nonce: 0,
|
||||
deadline: 0,
|
||||
additionalValidationContract: address(0),
|
||||
additionalValidationData: ""
|
||||
}),
|
||||
input: InputToken({
|
||||
token: address(WETH_ADDR),
|
||||
amount: amountIn,
|
||||
maxAmount: amountIn
|
||||
}),
|
||||
outputs: outputs,
|
||||
sig: "",
|
||||
hash: ""
|
||||
});
|
||||
|
||||
vm.startPrank(REACTOR);
|
||||
filler.reactorCallback(orders, callbackData);
|
||||
vm.stopPrank();
|
||||
|
||||
// Check that the funds are in the filler at the end of the function call
|
||||
uint256 finalBalance = IERC20(DAI_ADDR).balanceOf(address(filler));
|
||||
assertGe(finalBalance, amountOut);
|
||||
|
||||
// Check that the proper approval was set
|
||||
vm.startPrank(REACTOR);
|
||||
IERC20(DAI_ADDR).transferFrom(address(filler), BOB, amountOut);
|
||||
vm.stopPrank();
|
||||
assertGe(IERC20(DAI_ADDR).balanceOf(BOB), amountOut);
|
||||
}
|
||||
|
||||
function testExecuteIntegration() public {
|
||||
fillerSetup();
|
||||
|
||||
// Set to time with no more penalty for not being exclusive filler
|
||||
vm.warp(1752050415);
|
||||
|
||||
deal(
|
||||
DAI_ADDR,
|
||||
address(0xD213e6F6dCB2DBaC03FA28b893F6dA1BD822e852),
|
||||
2000 ether
|
||||
);
|
||||
|
||||
uint256 amountIn = 2000000000000000000000;
|
||||
|
||||
vm.startPrank(address(0xD213e6F6dCB2DBaC03FA28b893F6dA1BD822e852));
|
||||
// Approve Permit2
|
||||
IERC20(DAI_ADDR).approve(
|
||||
address(0x000000000022D473030F116dDEE9F6B43aC78BA3), amountIn
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
// Tx 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d
|
||||
// Calldata generated using rust test `test_sequential_swap_usx`
|
||||
|
||||
SignedOrder memory order = SignedOrder({
|
||||
order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e8520468320351debb1ddbfb032a239d699e3d54e3ce2b6e1037cd836a784c80b60100000000000000000000000000000000000000000000000000000000686e2bf9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000076f9f4870000000000000000000000000000000000000000000000000000000076566300000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e85200000000000000000000000000000000000000000000000000000000686e2aee00000000000000000000000000000000000000000000000000000000686e2b2a000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000007727b5f40000000000000000000000000000000000000000000000000000000000000041a2d261cd4c8930428260f18b55e3036024bac68d58cb2ee6161e6395b0984b827104158713d44ddc4e14d852b48d93d95a4e60b8d5be1ef431c1e82d2f76a4111b00000000000000000000000000000000000000000000000000000000000000",
|
||||
sig: hex"f4cc5734820e4ee08519045c83a25b75687756053b3d6c0fda2141380dfa6ef17b40f64d9279f237e96982c6ba53a202e01a4358fd66e027c9bdf200d5626f441c"
|
||||
});
|
||||
|
||||
bytes memory callbackData =
|
||||
loadCallDataFromFile("test_sequential_swap_usx");
|
||||
|
||||
vm.startPrank(EXECUTOR);
|
||||
filler.execute(order, callbackData);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testExecute() public {
|
||||
fillerSetup();
|
||||
|
||||
// Set to time with no more penalty for not being exclusive filler
|
||||
vm.warp(1752050415);
|
||||
|
||||
// tx: 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d
|
||||
// DAI ──> USDT
|
||||
SignedOrder memory order = SignedOrder({
|
||||
order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e8520468320351debb1ddbfb032a239d699e3d54e3ce2b6e1037cd836a784c80b60100000000000000000000000000000000000000000000000000000000686e2bf9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000076f9f4870000000000000000000000000000000000000000000000000000000076566300000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e85200000000000000000000000000000000000000000000000000000000686e2aee00000000000000000000000000000000000000000000000000000000686e2b2a000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000007727b5f40000000000000000000000000000000000000000000000000000000000000041a2d261cd4c8930428260f18b55e3036024bac68d58cb2ee6161e6395b0984b827104158713d44ddc4e14d852b48d93d95a4e60b8d5be1ef431c1e82d2f76a4111b00000000000000000000000000000000000000000000000000000000000000",
|
||||
sig: hex"f4cc5734820e4ee08519045c83a25b75687756053b3d6c0fda2141380dfa6ef17b40f64d9279f237e96982c6ba53a202e01a4358fd66e027c9bdf200d5626f441c"
|
||||
});
|
||||
|
||||
uint256 amountIn = 2000000000000000000000;
|
||||
bool zeroForOne = true;
|
||||
uint24 fee = 100;
|
||||
bytes memory protocolData = abi.encodePacked(
|
||||
DAI_ADDR,
|
||||
USDT_ADDR,
|
||||
fee,
|
||||
fillerAddr,
|
||||
DAI_USDT_USV3,
|
||||
zeroForOne,
|
||||
RestrictTransferFrom.TransferType.TransferFrom
|
||||
);
|
||||
|
||||
bytes memory swap =
|
||||
encodeSingleSwap(address(usv3Executor), protocolData);
|
||||
|
||||
bytes memory tychoRouterData = abi.encodeWithSelector(
|
||||
tychoRouter.singleSwap.selector,
|
||||
amountIn,
|
||||
DAI_ADDR,
|
||||
USDT_ADDR,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
fillerAddr,
|
||||
true,
|
||||
swap
|
||||
);
|
||||
|
||||
bytes memory callbackData = abi.encodePacked(
|
||||
true, // tokenIn approval needed
|
||||
true, // tokenOut approval needed
|
||||
tychoRouterData
|
||||
);
|
||||
|
||||
vm.startPrank(address(filler));
|
||||
IERC20(WBTC_ADDR).approve(tychoRouterAddr, amountIn);
|
||||
vm.stopPrank();
|
||||
|
||||
vm.startPrank(EXECUTOR);
|
||||
filler.execute(order, callbackData);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -187,10 +187,10 @@ mod tests {
|
||||
signers::local::PrivateKeySigner,
|
||||
};
|
||||
use num_bigint::BigUint;
|
||||
use tycho_common::models::Chain as TychoCommonChain;
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::{evm::encoding_utils::sign_permit, models::Chain};
|
||||
use crate::encoding::evm::encoding_utils::sign_permit;
|
||||
|
||||
// These two implementations are to avoid comparing the expiration and sig_deadline fields
|
||||
// because they are timestamps
|
||||
@@ -224,7 +224,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -334,7 +334,7 @@ mod tests {
|
||||
let sol_permit: PermitSingle =
|
||||
PermitSingle::try_from(&permit).expect("Failed to convert to PermitSingle");
|
||||
|
||||
let signature = sign_permit(eth_chain().id, &permit, signer).unwrap();
|
||||
let signature = sign_permit(eth_chain().id(), &permit, signer).unwrap();
|
||||
let encoded =
|
||||
(bytes_to_address(&anvil_account).unwrap(), sol_permit, signature.as_bytes().to_vec())
|
||||
.abi_encode();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use alloy::{primitives::B256, signers::local::PrivateKeySigner};
|
||||
use tycho_common::{models::Chain as TychoCommonChain, Bytes};
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -10,7 +10,7 @@ use crate::encoding::{
|
||||
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
|
||||
tycho_encoders::{TychoExecutorEncoder, TychoRouterEncoder},
|
||||
},
|
||||
models::{Chain, UserTransferType},
|
||||
models::UserTransferType,
|
||||
tycho_encoder::TychoEncoder,
|
||||
};
|
||||
|
||||
@@ -41,8 +41,8 @@ impl TychoRouterEncoderBuilder {
|
||||
user_transfer_type: None,
|
||||
}
|
||||
}
|
||||
pub fn chain(mut self, chain: TychoCommonChain) -> Self {
|
||||
self.chain = Some(chain.into());
|
||||
pub fn chain(mut self, chain: Chain) -> Self {
|
||||
self.chain = Some(chain);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -85,10 +85,10 @@ impl TychoRouterEncoderBuilder {
|
||||
if let Some(address) = self.router_address {
|
||||
tycho_router_address = address;
|
||||
} else {
|
||||
let default_routers: HashMap<String, Bytes> =
|
||||
let default_routers: HashMap<Chain, Bytes> =
|
||||
serde_json::from_str(DEFAULT_ROUTERS_JSON)?;
|
||||
tycho_router_address = default_routers
|
||||
.get(&chain.name)
|
||||
.get(&chain)
|
||||
.ok_or(EncodingError::FatalError(
|
||||
"No default router address found for chain".to_string(),
|
||||
))?
|
||||
@@ -96,7 +96,7 @@ impl TychoRouterEncoderBuilder {
|
||||
}
|
||||
|
||||
let swap_encoder_registry =
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain.clone())?;
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?;
|
||||
|
||||
let signer = if let Some(pk) = self.swapper_pk {
|
||||
let pk = B256::from_str(&pk).map_err(|_| {
|
||||
@@ -141,8 +141,8 @@ impl TychoExecutorEncoderBuilder {
|
||||
pub fn new() -> Self {
|
||||
TychoExecutorEncoderBuilder { chain: None, executors_file_path: None }
|
||||
}
|
||||
pub fn chain(mut self, chain: TychoCommonChain) -> Self {
|
||||
self.chain = Some(chain.into());
|
||||
pub fn chain(mut self, chain: Chain) -> Self {
|
||||
self.chain = Some(chain);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -158,7 +158,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.clone())?;
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?;
|
||||
Ok(Box::new(TychoExecutorEncoder::new(swap_encoder_registry)?))
|
||||
} else {
|
||||
Err(EncodingError::FatalError(
|
||||
|
||||
@@ -11,20 +11,30 @@ use crate::encoding::{evm::constants::GROUPABLE_PROTOCOLS, models::Swap};
|
||||
/// * `protocol_system`: String, the protocol system of the swaps
|
||||
/// * `swaps`: Vec<Swap>, the sequence of swaps to be executed as a group
|
||||
/// * `split`: f64, the split percentage of the first swap in the group
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct SwapGroup {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SwapGroup<'a> {
|
||||
pub token_in: Bytes,
|
||||
pub token_out: Bytes,
|
||||
pub protocol_system: String,
|
||||
pub swaps: Vec<Swap>,
|
||||
pub swaps: Vec<Swap<'a>>,
|
||||
pub split: f64,
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for SwapGroup<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.token_in == other.token_in &&
|
||||
self.token_out == other.token_out &&
|
||||
self.protocol_system == other.protocol_system &&
|
||||
self.swaps == other.swaps &&
|
||||
self.split == other.split
|
||||
}
|
||||
}
|
||||
|
||||
/// Group consecutive swaps which can be encoded into one swap execution for gas optimization.
|
||||
///
|
||||
/// An example where this applies is the case of USV4, which uses a PoolManager contract
|
||||
/// to save token transfers on consecutive swaps.
|
||||
pub fn group_swaps(swaps: &Vec<Swap>) -> Vec<SwapGroup> {
|
||||
pub fn group_swaps<'a>(swaps: &'a Vec<Swap<'a>>) -> Vec<SwapGroup<'a>> {
|
||||
let mut grouped_swaps: Vec<SwapGroup> = Vec::new();
|
||||
let mut current_group: Option<SwapGroup> = None;
|
||||
let mut last_swap_protocol = "".to_string();
|
||||
@@ -106,6 +116,7 @@ mod tests {
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -116,6 +127,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_usdc_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -126,12 +138,10 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let grouped_swaps = group_swaps(&vec![
|
||||
swap_weth_wbtc.clone(),
|
||||
swap_wbtc_usdc.clone(),
|
||||
swap_usdc_dai.clone(),
|
||||
]);
|
||||
let swaps = vec![swap_weth_wbtc.clone(), swap_wbtc_usdc.clone(), swap_usdc_dai.clone()];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
assert_eq!(
|
||||
grouped_swaps,
|
||||
@@ -178,6 +188,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -188,6 +199,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -200,6 +212,7 @@ mod tests {
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -210,13 +223,15 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let grouped_swaps = group_swaps(&vec![
|
||||
let swaps = vec![
|
||||
swap_wbtc_weth.clone(),
|
||||
swap_weth_usdc.clone(),
|
||||
swap_weth_dai.clone(),
|
||||
swap_dai_usdc.clone(),
|
||||
]);
|
||||
];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
assert_eq!(
|
||||
grouped_swaps,
|
||||
@@ -269,6 +284,7 @@ mod tests {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -279,6 +295,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -291,6 +308,7 @@ mod tests {
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -301,14 +319,16 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let grouped_swaps = group_swaps(&vec![
|
||||
let swaps = vec![
|
||||
swap_weth_wbtc.clone(),
|
||||
swap_wbtc_usdc.clone(),
|
||||
swap_weth_dai.clone(),
|
||||
swap_dai_usdc.clone(),
|
||||
]);
|
||||
];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
assert_eq!(
|
||||
grouped_swaps,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use alloy::primitives::{aliases::U24, U8};
|
||||
use tycho_common::Bytes;
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -14,7 +14,7 @@ use crate::encoding::{
|
||||
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
|
||||
utils::{get_token_position, percentage_to_uint24, ple_encode},
|
||||
},
|
||||
models::{Chain, EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType},
|
||||
models::{EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
@@ -52,8 +52,8 @@ impl SingleSwapStrategyEncoder {
|
||||
swap_encoder_registry,
|
||||
router_address: router_address.clone(),
|
||||
transfer_optimization: TransferOptimization::new(
|
||||
chain.native_token()?,
|
||||
chain.wrapped_token()?,
|
||||
chain.native_token().address,
|
||||
chain.wrapped_native_token().address,
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
@@ -195,16 +195,18 @@ impl SequentialSwapStrategyEncoder {
|
||||
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
|
||||
|
||||
}.to_string();
|
||||
let native_token_address = chain.native_token().address;
|
||||
let wrapped_token_address = chain.wrapped_native_token().address;
|
||||
Ok(Self {
|
||||
function_signature,
|
||||
swap_encoder_registry,
|
||||
router_address: router_address.clone(),
|
||||
native_address: chain.native_token()?,
|
||||
wrapped_address: chain.wrapped_token()?,
|
||||
native_address: native_token_address.clone(),
|
||||
wrapped_address: wrapped_token_address.clone(),
|
||||
sequential_swap_validator: SequentialSwapValidator,
|
||||
transfer_optimization: TransferOptimization::new(
|
||||
chain.native_token()?,
|
||||
chain.wrapped_token()?,
|
||||
native_token_address,
|
||||
wrapped_token_address,
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
@@ -356,16 +358,18 @@ impl SplitSwapStrategyEncoder {
|
||||
} else {
|
||||
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
|
||||
}.to_string();
|
||||
let native_token_address = chain.native_token().address;
|
||||
let wrapped_token_address = chain.wrapped_native_token().address;
|
||||
Ok(Self {
|
||||
function_signature,
|
||||
swap_encoder_registry,
|
||||
native_address: chain.native_token()?,
|
||||
wrapped_address: chain.wrapped_token()?,
|
||||
native_address: native_token_address.clone(),
|
||||
wrapped_address: wrapped_token_address.clone(),
|
||||
split_swap_validator: SplitSwapValidator,
|
||||
router_address: router_address.clone(),
|
||||
transfer_optimization: TransferOptimization::new(
|
||||
chain.native_token()?,
|
||||
chain.wrapped_token()?,
|
||||
native_token_address,
|
||||
wrapped_token_address,
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
@@ -535,7 +539,7 @@ mod tests {
|
||||
use alloy::{hex::encode, primitives::hex};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::{
|
||||
models::{protocol::ProtocolComponent, Chain as TychoCommonChain},
|
||||
models::{protocol::ProtocolComponent, Chain},
|
||||
Bytes,
|
||||
};
|
||||
|
||||
@@ -543,7 +547,7 @@ mod tests {
|
||||
use crate::encoding::models::Swap;
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
fn weth() -> Bytes {
|
||||
@@ -581,6 +585,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SingleSwapStrategyEncoder::new(
|
||||
@@ -642,6 +647,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SingleSwapStrategyEncoder::new(
|
||||
@@ -713,6 +719,7 @@ mod tests {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -724,6 +731,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SequentialSwapStrategyEncoder::new(
|
||||
@@ -816,6 +824,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0.6f64, // 60% of input
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// USDC -> WETH (Pool 2) - 40% of input (remaining)
|
||||
@@ -838,6 +847,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None, // Remaining 40%
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// WETH -> USDC (Pool 2)
|
||||
@@ -860,6 +870,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
@@ -968,6 +979,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_weth_usdc_v3_pool1 = Swap {
|
||||
@@ -989,6 +1001,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.6f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_weth_usdc_v3_pool2 = Swap {
|
||||
@@ -1010,6 +1023,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
|
||||
@@ -215,6 +215,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let result = validator.validate_swap_path(&swaps, &weth, &dai, &None, ð, &weth);
|
||||
assert_eq!(result, Ok(()));
|
||||
@@ -238,6 +239,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -249,6 +251,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
let result = validator.validate_swap_path(&swaps, &weth, &usdc, &None, ð, &weth);
|
||||
@@ -275,6 +278,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
// This swap is disconnected from the WETH->DAI path
|
||||
Swap {
|
||||
@@ -287,6 +291,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
let result =
|
||||
@@ -315,6 +320,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -326,6 +332,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -352,6 +359,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 1.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let result =
|
||||
validator.validate_swap_path(&unreachable_swaps, &weth, &usdc, &None, ð, &weth);
|
||||
@@ -391,6 +399,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let result = validator.validate_split_percentages(&swaps);
|
||||
assert_eq!(result, Ok(()));
|
||||
@@ -414,6 +423,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -425,6 +435,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.3,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -436,6 +447,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.0, // Remainder (20%)
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(validator
|
||||
@@ -460,6 +472,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.7,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -471,6 +484,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.3,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(matches!(
|
||||
@@ -496,6 +510,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -507,6 +522,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(matches!(
|
||||
@@ -532,6 +548,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.6,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -543,6 +560,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -554,6 +572,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(matches!(
|
||||
@@ -579,6 +598,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
|
||||
let result = validator.validate_swap_path(
|
||||
@@ -609,6 +629,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
|
||||
let result = validator.validate_swap_path(
|
||||
|
||||
@@ -179,6 +179,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let swap = SwapGroup {
|
||||
protocol_system: protocol,
|
||||
@@ -241,6 +242,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}],
|
||||
})
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::swap_encoder::swap_encoders::{
|
||||
BalancerV2SwapEncoder, BalancerV3SwapEncoder, CurveSwapEncoder, EkuboSwapEncoder,
|
||||
MaverickV2SwapEncoder, UniswapV2SwapEncoder, UniswapV3SwapEncoder, UniswapV4SwapEncoder,
|
||||
},
|
||||
models::Chain,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::{collections::HashMap, fs};
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
constants::{DEFAULT_EXECUTORS_JSON, PROTOCOL_SPECIFIC_CONFIG},
|
||||
swap_encoder::builder::SwapEncoderBuilder,
|
||||
},
|
||||
models::Chain,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
@@ -30,15 +31,15 @@ impl SwapEncoderRegistry {
|
||||
} else {
|
||||
DEFAULT_EXECUTORS_JSON.to_string()
|
||||
};
|
||||
let config: HashMap<String, HashMap<String, String>> = serde_json::from_str(&config_str)?;
|
||||
let config: HashMap<Chain, HashMap<String, String>> = serde_json::from_str(&config_str)?;
|
||||
let executors = config
|
||||
.get(&chain.name)
|
||||
.get(&chain)
|
||||
.ok_or(EncodingError::FatalError("No executors found for chain".to_string()))?;
|
||||
|
||||
let protocol_specific_config: HashMap<String, HashMap<String, HashMap<String, String>>> =
|
||||
let protocol_specific_config: HashMap<Chain, HashMap<String, HashMap<String, String>>> =
|
||||
serde_json::from_str(PROTOCOL_SPECIFIC_CONFIG)?;
|
||||
let protocol_specific_config = protocol_specific_config
|
||||
.get(&chain.name)
|
||||
.get(&chain)
|
||||
.ok_or(EncodingError::FatalError(
|
||||
"No protocol specific config found for chain".to_string(),
|
||||
))?;
|
||||
@@ -47,7 +48,7 @@ impl SwapEncoderRegistry {
|
||||
let builder = SwapEncoderBuilder::new(
|
||||
protocol,
|
||||
executor_address,
|
||||
chain.clone(),
|
||||
chain,
|
||||
protocol_specific_config
|
||||
.get(protocol)
|
||||
.cloned(),
|
||||
|
||||
@@ -5,7 +5,7 @@ use alloy::{
|
||||
sol_types::SolValue,
|
||||
};
|
||||
use serde_json::from_str;
|
||||
use tycho_common::Bytes;
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -13,7 +13,7 @@ use crate::encoding::{
|
||||
approvals::protocol_approvals_manager::ProtocolApprovalsManager,
|
||||
utils::{bytes_to_address, get_static_attribute, pad_to_fixed_size},
|
||||
},
|
||||
models::{Chain, EncodingContext, Swap},
|
||||
models::{EncodingContext, Swap},
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
@@ -428,7 +428,10 @@ impl CurveSwapEncoder {
|
||||
// Some curve pools support both ETH and WETH as tokens.
|
||||
// They do the wrapping/unwrapping inside the pool
|
||||
fn normalize_token(&self, token: Address, coins: &[Address]) -> Result<Address, EncodingError> {
|
||||
let native_token_address = bytes_to_address(&self.native_token_address)?;
|
||||
let native_token_address =
|
||||
Address::from_str(&self.native_token_curve_address).map_err(|_| {
|
||||
EncodingError::FatalError("Invalid native token curve address".to_string())
|
||||
})?;
|
||||
let wrapped_native_token_address = bytes_to_address(&self.wrapped_native_token_address)?;
|
||||
if token == native_token_address && !coins.contains(&token) {
|
||||
Ok(wrapped_native_token_address)
|
||||
@@ -461,7 +464,7 @@ impl CurveSwapEncoder {
|
||||
.iter()
|
||||
.position(|&addr| addr == token_out)
|
||||
.ok_or(EncodingError::FatalError(format!(
|
||||
"Token in address {token_in} not found in curve pool coins"
|
||||
"Token in address {token_out} not found in curve pool coins"
|
||||
)))?;
|
||||
Ok((U8::from(i), U8::from(j)))
|
||||
}
|
||||
@@ -484,9 +487,9 @@ impl SwapEncoder for CurveSwapEncoder {
|
||||
.to_string();
|
||||
Ok(Self {
|
||||
executor_address,
|
||||
native_token_address: chain.native_token()?,
|
||||
native_token_address: chain.native_token().address,
|
||||
native_token_curve_address,
|
||||
wrapped_native_token_address: chain.wrapped_token()?,
|
||||
wrapped_native_token_address: chain.wrapped_native_token().address,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -664,7 +667,7 @@ mod tests {
|
||||
use alloy::hex::encode;
|
||||
use num_bigint::BigInt;
|
||||
use tycho_common::{
|
||||
models::{protocol::ProtocolComponent, Chain as TychoCoreChain},
|
||||
models::{protocol::ProtocolComponent, Chain},
|
||||
Bytes,
|
||||
};
|
||||
|
||||
@@ -688,6 +691,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: Bytes::from("0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e"), // BOB
|
||||
@@ -699,7 +703,7 @@ mod tests {
|
||||
};
|
||||
let encoder = UniswapV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -748,6 +752,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: Bytes::from("0x0000000000000000000000000000000000000001"),
|
||||
@@ -759,7 +764,7 @@ mod tests {
|
||||
};
|
||||
let encoder = UniswapV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -809,6 +814,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -821,7 +827,7 @@ mod tests {
|
||||
};
|
||||
let encoder = BalancerV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
Some(HashMap::from([(
|
||||
"vault_address".to_string(),
|
||||
"0xba12222222228d8ba445958a75a0704d566bf2c8".to_string(),
|
||||
@@ -882,6 +888,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver is ALICE to match the solidity tests
|
||||
@@ -896,7 +903,7 @@ mod tests {
|
||||
};
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -957,6 +964,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
@@ -971,7 +979,7 @@ mod tests {
|
||||
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1054,6 +1062,7 @@ mod tests {
|
||||
token_out: usdt_address.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let second_swap = Swap {
|
||||
@@ -1062,11 +1071,12 @@ mod tests {
|
||||
token_out: wbtc_address.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1147,6 +1157,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
@@ -1158,9 +1169,7 @@ mod tests {
|
||||
transfer_type: TransferType::Transfer,
|
||||
};
|
||||
|
||||
let encoder =
|
||||
EkuboSwapEncoder::new(String::default(), TychoCoreChain::Ethereum.into(), None)
|
||||
.unwrap();
|
||||
let encoder = EkuboSwapEncoder::new(String::default(), Chain::Ethereum, None).unwrap();
|
||||
|
||||
let encoded_swap = encoder
|
||||
.encode_swap(&swap, &encoding_context)
|
||||
@@ -1191,9 +1200,7 @@ mod tests {
|
||||
let group_token_out = Bytes::from("0xdAC17F958D2ee523a2206206994597C13D831ec7"); // USDT
|
||||
let intermediary_token = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC
|
||||
|
||||
let encoder =
|
||||
EkuboSwapEncoder::new(String::default(), TychoCoreChain::Ethereum.into(), None)
|
||||
.unwrap();
|
||||
let encoder = EkuboSwapEncoder::new(String::default(), Chain::Ethereum, None).unwrap();
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: RECEIVER.into(),
|
||||
@@ -1220,6 +1227,7 @@ mod tests {
|
||||
token_out: intermediary_token.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let second_swap = Swap {
|
||||
@@ -1236,6 +1244,7 @@ mod tests {
|
||||
token_out: group_token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let first_encoded_swap = encoder
|
||||
@@ -1356,13 +1365,10 @@ mod tests {
|
||||
token_out: Bytes::from(token_out),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::default(),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
curve_config(),
|
||||
)
|
||||
.unwrap();
|
||||
let encoder =
|
||||
CurveSwapEncoder::new(String::default(), Chain::Ethereum, curve_config()).unwrap();
|
||||
let (i, j) = encoder
|
||||
.get_coin_indexes(
|
||||
&swap,
|
||||
@@ -1400,6 +1406,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1412,7 +1419,7 @@ mod tests {
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
curve_config(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1472,6 +1479,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1484,7 +1492,7 @@ mod tests {
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
curve_config(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1545,6 +1553,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1557,7 +1566,7 @@ mod tests {
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
Some(HashMap::from([
|
||||
(
|
||||
"native_token_address".to_string(),
|
||||
@@ -1619,6 +1628,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1631,7 +1641,7 @@ mod tests {
|
||||
};
|
||||
let encoder = BalancerV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1677,6 +1687,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1689,7 +1700,7 @@ mod tests {
|
||||
};
|
||||
let encoder = MaverickV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use alloy::signers::local::PrivateKeySigner;
|
||||
use tycho_common::Bytes;
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -17,7 +17,7 @@ use crate::encoding::{
|
||||
utils::ple_encode,
|
||||
},
|
||||
models::{
|
||||
Chain, EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType,
|
||||
EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType,
|
||||
UserTransferType,
|
||||
},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
@@ -62,19 +62,19 @@ impl TychoRouterEncoder {
|
||||
};
|
||||
Ok(TychoRouterEncoder {
|
||||
single_swap_strategy: SingleSwapStrategyEncoder::new(
|
||||
chain.clone(),
|
||||
chain,
|
||||
swap_encoder_registry.clone(),
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
)?,
|
||||
sequential_swap_strategy: SequentialSwapStrategyEncoder::new(
|
||||
chain.clone(),
|
||||
chain,
|
||||
swap_encoder_registry.clone(),
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
)?,
|
||||
split_swap_strategy: SplitSwapStrategyEncoder::new(
|
||||
chain.clone(),
|
||||
chain,
|
||||
swap_encoder_registry,
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
@@ -154,11 +154,11 @@ impl TychoEncoder for TychoRouterEncoder {
|
||||
let encoded_solution = self.encode_solution(solution)?;
|
||||
|
||||
let transaction = encode_tycho_router_call(
|
||||
self.chain.id,
|
||||
self.chain.id(),
|
||||
encoded_solution,
|
||||
solution,
|
||||
&self.user_transfer_type,
|
||||
&self.chain.native_token()?,
|
||||
&self.chain.native_token().address,
|
||||
self.signer.clone(),
|
||||
)?;
|
||||
|
||||
@@ -187,8 +187,11 @@ impl TychoEncoder for TychoRouterEncoder {
|
||||
if solution.swaps.is_empty() {
|
||||
return Err(EncodingError::FatalError("No swaps found in solution".to_string()));
|
||||
}
|
||||
let native_address = self.chain.native_token()?;
|
||||
let wrapped_address = self.chain.wrapped_token()?;
|
||||
let native_address = self.chain.native_token().address;
|
||||
let wrapped_address = self
|
||||
.chain
|
||||
.wrapped_native_token()
|
||||
.address;
|
||||
if let Some(native_action) = &solution.native_action {
|
||||
if native_action == &NativeAction::Wrap {
|
||||
if solution.given_token != native_address {
|
||||
@@ -400,7 +403,7 @@ mod tests {
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCommonChain};
|
||||
use tycho_common::models::{protocol::ProtocolComponent, Chain};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
@@ -432,7 +435,7 @@ mod tests {
|
||||
// Fee and tick spacing information for this test is obtained by querying the
|
||||
// USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e
|
||||
// Using the poolKeys function with the first 25 bytes of the pool id
|
||||
fn swap_usdc_eth_univ4() -> Swap {
|
||||
fn swap_usdc_eth_univ4() -> Swap<'static> {
|
||||
let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be());
|
||||
let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be());
|
||||
let mut static_attributes_usdc_eth: HashMap<String, Bytes> = HashMap::new();
|
||||
@@ -450,10 +453,11 @@ mod tests {
|
||||
token_out: eth().clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_eth_pepe_univ4() -> Swap {
|
||||
fn swap_eth_pepe_univ4() -> Swap<'static> {
|
||||
let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be());
|
||||
let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be());
|
||||
let mut static_attributes_eth_pepe: HashMap<String, Bytes> = HashMap::new();
|
||||
@@ -471,6 +475,7 @@ mod tests {
|
||||
token_out: pepe().clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +484,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
|
||||
@@ -519,6 +524,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -584,6 +590,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_dai_usdc = Swap {
|
||||
@@ -596,6 +603,7 @@ mod tests {
|
||||
token_out: usdc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -676,6 +684,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -705,6 +714,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -739,6 +749,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -793,6 +804,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -821,6 +833,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -856,6 +869,7 @@ mod tests {
|
||||
token_out: eth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -896,6 +910,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -907,6 +922,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -918,6 +934,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -951,6 +968,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -962,6 +980,7 @@ mod tests {
|
||||
token_out: usdc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -973,6 +992,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -984,6 +1004,7 @@ mod tests {
|
||||
token_out: wbtc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1024,6 +1045,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -1035,6 +1057,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -1046,6 +1069,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1079,6 +1103,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -1090,6 +1115,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1144,6 +1170,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -1204,6 +1231,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use clap::ValueEnum;
|
||||
use hex;
|
||||
use num_bigint::BigUint;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tycho_common::{
|
||||
models::{protocol::ProtocolComponent, Chain as TychoCommonChain},
|
||||
Bytes,
|
||||
models::protocol::ProtocolComponent, simulation::protocol_sim::ProtocolSim, Bytes,
|
||||
};
|
||||
|
||||
use crate::encoding::{errors::EncodingError, serde_primitives::biguint_string};
|
||||
use crate::encoding::serde_primitives::biguint_string;
|
||||
|
||||
/// Specifies the method for transferring user funds into Tycho execution.
|
||||
///
|
||||
@@ -37,7 +35,7 @@ pub enum UserTransferType {
|
||||
/// Represents a solution containing details describing an order, and instructions for filling
|
||||
/// the order.
|
||||
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
|
||||
pub struct Solution {
|
||||
pub struct Solution<'a> {
|
||||
/// Address of the sender.
|
||||
pub sender: Bytes,
|
||||
/// Address of the receiver.
|
||||
@@ -57,7 +55,7 @@ pub struct Solution {
|
||||
#[serde(with = "biguint_string")]
|
||||
pub checked_amount: BigUint,
|
||||
/// List of swaps to fulfill the solution.
|
||||
pub swaps: Vec<Swap>,
|
||||
pub swaps: Vec<Swap<'a>>,
|
||||
/// If set, the corresponding native action will be executed.
|
||||
pub native_action: Option<NativeAction>,
|
||||
}
|
||||
@@ -75,8 +73,8 @@ pub enum NativeAction {
|
||||
}
|
||||
|
||||
/// Represents a swap operation to be performed on a pool.
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Swap {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Swap<'a> {
|
||||
/// Protocol component from tycho indexer
|
||||
pub component: ProtocolComponent,
|
||||
/// Token being input into the pool.
|
||||
@@ -88,17 +86,32 @@ pub struct Swap {
|
||||
pub split: f64,
|
||||
/// Optional user data to be passed to encoding.
|
||||
pub user_data: Option<Bytes>,
|
||||
/// Optional protocol state used to perform the swap.
|
||||
#[serde(skip)]
|
||||
pub protocol_state: Option<&'a dyn ProtocolSim>,
|
||||
}
|
||||
|
||||
impl Swap {
|
||||
impl<'a> Swap<'a> {
|
||||
pub fn new<T: Into<ProtocolComponent>>(
|
||||
component: T,
|
||||
token_in: Bytes,
|
||||
token_out: Bytes,
|
||||
split: f64,
|
||||
user_data: Option<Bytes>,
|
||||
protocol_state: Option<&'a dyn ProtocolSim>,
|
||||
) -> Self {
|
||||
Self { component: component.into(), token_in, token_out, split, user_data }
|
||||
Self { component: component.into(), token_in, token_out, split, user_data, protocol_state }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Swap<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.component == other.component &&
|
||||
self.token_in == other.token_in &&
|
||||
self.token_out == other.token_out &&
|
||||
self.split == other.split &&
|
||||
self.user_data == other.user_data
|
||||
// Skip protocol_state comparison since trait objects don't implement PartialEq
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,63 +223,6 @@ pub enum TransferType {
|
||||
None = 2,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Chain {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<TychoCommonChain> for Chain {
|
||||
fn from(chain: TychoCommonChain) -> Self {
|
||||
match chain {
|
||||
TychoCommonChain::Ethereum => Chain { id: 1, name: chain.to_string() },
|
||||
TychoCommonChain::ZkSync => Chain { id: 324, name: chain.to_string() },
|
||||
TychoCommonChain::Arbitrum => Chain { id: 42161, name: chain.to_string() },
|
||||
TychoCommonChain::Starknet => Chain { id: 0, name: chain.to_string() },
|
||||
TychoCommonChain::Base => Chain { id: 8453, name: chain.to_string() },
|
||||
TychoCommonChain::Unichain => Chain { id: 130, name: chain.to_string() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Chain {
|
||||
fn decode_hex(&self, hex_str: &str, err_msg: &str) -> Result<Bytes, EncodingError> {
|
||||
Ok(Bytes::from(
|
||||
hex::decode(hex_str).map_err(|_| EncodingError::FatalError(err_msg.to_string()))?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn native_token(&self) -> Result<Bytes, EncodingError> {
|
||||
let decode_err_msg = "Failed to decode native token";
|
||||
match self.id {
|
||||
1 | 8453 | 42161 => {
|
||||
self.decode_hex("0000000000000000000000000000000000000000", decode_err_msg)
|
||||
}
|
||||
324 => self.decode_hex("000000000000000000000000000000000000800A", decode_err_msg),
|
||||
130 => self.decode_hex("0000000000000000000000000000000000000000", decode_err_msg),
|
||||
_ => Err(EncodingError::InvalidInput(format!(
|
||||
"Native token not set for chain {:?}. Double check the chain is supported.",
|
||||
self.name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrapped_token(&self) -> Result<Bytes, EncodingError> {
|
||||
let decode_err_msg = "Failed to decode wrapped token";
|
||||
match self.id {
|
||||
1 => self.decode_hex("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decode_err_msg),
|
||||
8453 => self.decode_hex("4200000000000000000000000000000000000006", decode_err_msg),
|
||||
324 => self.decode_hex("5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", decode_err_msg),
|
||||
42161 => self.decode_hex("82aF49447D8a07e3bd95BD0d56f35241523fBab1", decode_err_msg),
|
||||
130 => self.decode_hex("4200000000000000000000000000000000000006", decode_err_msg),
|
||||
_ => Err(EncodingError::InvalidInput(format!(
|
||||
"Wrapped token not set for chain {:?}. Double check the chain is supported.",
|
||||
self.name
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -299,8 +255,14 @@ mod tests {
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
};
|
||||
let user_data = Some(Bytes::from("0x1234"));
|
||||
let swap =
|
||||
Swap::new(component, Bytes::from("0x12"), Bytes::from("34"), 0.5, user_data.clone());
|
||||
let swap = Swap::new(
|
||||
component,
|
||||
Bytes::from("0x12"),
|
||||
Bytes::from("34"),
|
||||
0.5,
|
||||
user_data.clone(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(swap.token_in, Bytes::from("0x12"));
|
||||
assert_eq!(swap.token_out, Bytes::from("0x34"));
|
||||
assert_eq!(swap.component.protocol_system, "uniswap_v2");
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
models::{Chain, EncodingContext, Swap},
|
||||
models::{EncodingContext, Swap},
|
||||
};
|
||||
|
||||
/// A trait for protocol-specific swap encoding, where each implementation should handle the
|
||||
|
||||
@@ -4,10 +4,9 @@ pub mod encoding;
|
||||
use std::str::FromStr;
|
||||
|
||||
use alloy::{primitives::B256, signers::local::PrivateKeySigner};
|
||||
use tycho_common::{models::Chain as TychoCommonChain, Bytes};
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
use tycho_execution::encoding::{
|
||||
evm::encoder_builders::TychoRouterEncoderBuilder,
|
||||
models::{Chain, UserTransferType},
|
||||
evm::encoder_builders::TychoRouterEncoderBuilder, models::UserTransferType,
|
||||
tycho_encoder::TychoEncoder,
|
||||
};
|
||||
|
||||
@@ -16,7 +15,7 @@ pub fn router_address() -> Bytes {
|
||||
}
|
||||
|
||||
pub fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
pub fn eth() -> Bytes {
|
||||
@@ -43,6 +42,10 @@ pub fn pepe() -> Bytes {
|
||||
Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap()
|
||||
}
|
||||
|
||||
pub fn usdt() -> Bytes {
|
||||
Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap()
|
||||
}
|
||||
|
||||
pub fn get_signer() -> PrivateKeySigner {
|
||||
// Set up a mock private key for signing (Alice's pk in our contract tests)
|
||||
let private_key =
|
||||
|
||||
@@ -48,6 +48,7 @@ fn test_uniswap_v3_uniswap_v2() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -59,6 +60,7 @@ fn test_uniswap_v3_uniswap_v2() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -80,7 +82,7 @@ fn test_uniswap_v3_uniswap_v2() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -127,6 +129,7 @@ fn test_uniswap_v3_uniswap_v3() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -146,6 +149,7 @@ fn test_uniswap_v3_uniswap_v3() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -167,7 +171,7 @@ fn test_uniswap_v3_uniswap_v3() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -213,6 +217,7 @@ fn test_uniswap_v3_curve() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_wbtc_usdt = Swap {
|
||||
@@ -242,6 +247,7 @@ fn test_uniswap_v3_curve() {
|
||||
token_out: usdt.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -263,7 +269,7 @@ fn test_uniswap_v3_curve() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -301,6 +307,7 @@ fn test_balancer_v2_uniswap_v2() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_wbtc_usdc = Swap {
|
||||
@@ -313,6 +320,7 @@ fn test_balancer_v2_uniswap_v2() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -334,7 +342,7 @@ fn test_balancer_v2_uniswap_v2() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -375,6 +383,7 @@ fn test_multi_protocol() {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let balancer_swap_weth_wbtc = Swap {
|
||||
@@ -387,6 +396,7 @@ fn test_multi_protocol() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let curve_swap_wbtc_usdt = Swap {
|
||||
@@ -416,6 +426,7 @@ fn test_multi_protocol() {
|
||||
token_out: usdt.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// Ekubo
|
||||
@@ -438,6 +449,7 @@ fn test_multi_protocol() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// USV4
|
||||
@@ -461,6 +473,7 @@ fn test_multi_protocol() {
|
||||
token_out: eth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
@@ -490,7 +503,7 @@ fn test_multi_protocol() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -533,6 +546,7 @@ fn test_uniswap_v3_balancer_v3() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_qnt = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -544,6 +558,7 @@ fn test_uniswap_v3_balancer_v3() {
|
||||
token_out: qnt.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -565,7 +580,7 @@ fn test_uniswap_v3_balancer_v3() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
|
||||
@@ -40,6 +40,7 @@ fn test_single_encoding_strategy_ekubo() {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
@@ -63,7 +64,7 @@ fn test_single_encoding_strategy_ekubo() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -92,6 +93,7 @@ fn test_single_encoding_strategy_maverick() {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
@@ -115,7 +117,7 @@ fn test_single_encoding_strategy_maverick() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -156,6 +158,7 @@ fn test_single_encoding_strategy_usv4_eth_in() {
|
||||
token_out: pepe.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
|
||||
@@ -177,7 +180,7 @@ fn test_single_encoding_strategy_usv4_eth_in() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -222,6 +225,7 @@ fn test_single_encoding_strategy_usv4_eth_out() {
|
||||
token_out: eth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
@@ -244,7 +248,7 @@ fn test_single_encoding_strategy_usv4_eth_out() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -296,6 +300,7 @@ fn test_single_encoding_strategy_usv4_grouped_swap() {
|
||||
token_out: eth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_eth_pepe = Swap {
|
||||
@@ -309,6 +314,7 @@ fn test_single_encoding_strategy_usv4_grouped_swap() {
|
||||
token_out: pepe.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
|
||||
@@ -330,7 +336,7 @@ fn test_single_encoding_strategy_usv4_grouped_swap() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -420,6 +426,7 @@ fn test_single_encoding_strategy_curve() {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
@@ -443,7 +450,7 @@ fn test_single_encoding_strategy_curve() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -487,6 +494,7 @@ fn test_single_encoding_strategy_curve_st_eth() {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
@@ -510,7 +518,7 @@ fn test_single_encoding_strategy_curve_st_eth() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -540,6 +548,7 @@ fn test_single_encoding_strategy_balancer_v3() {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
@@ -563,7 +572,7 @@ fn test_single_encoding_strategy_balancer_v3() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
|
||||
@@ -37,6 +37,7 @@ fn test_sequential_swap_strategy_encoder() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -48,6 +49,7 @@ fn test_sequential_swap_strategy_encoder() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
|
||||
@@ -69,7 +71,7 @@ fn test_sequential_swap_strategy_encoder() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -103,6 +105,7 @@ fn test_sequential_swap_strategy_encoder_no_permit2() {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -114,6 +117,7 @@ fn test_sequential_swap_strategy_encoder_no_permit2() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -135,7 +139,7 @@ fn test_sequential_swap_strategy_encoder_no_permit2() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -215,6 +219,7 @@ fn test_sequential_strategy_cyclic_swap() {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// WETH -> USDC (Pool 2)
|
||||
@@ -237,6 +242,7 @@ fn test_sequential_strategy_cyclic_swap() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
@@ -260,7 +266,7 @@ fn test_sequential_strategy_cyclic_swap() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
|
||||
@@ -33,6 +33,7 @@ fn test_single_swap_strategy_encoder() {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
@@ -54,7 +55,7 @@ fn test_single_swap_strategy_encoder() {
|
||||
.unwrap();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solutions[0].clone(),
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -119,6 +120,7 @@ fn test_single_swap_strategy_encoder_no_permit2() {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
@@ -139,7 +141,7 @@ fn test_single_swap_strategy_encoder_no_permit2() {
|
||||
.unwrap()[0]
|
||||
.clone();
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
@@ -201,6 +203,7 @@ fn test_single_swap_strategy_encoder_no_transfer_in() {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::None);
|
||||
|
||||
@@ -221,7 +224,7 @@ fn test_single_swap_strategy_encoder_no_transfer_in() {
|
||||
.unwrap()[0]
|
||||
.clone();
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::None,
|
||||
@@ -284,6 +287,7 @@ fn test_single_swap_strategy_encoder_wrap() {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
|
||||
@@ -305,7 +309,7 @@ fn test_single_swap_strategy_encoder_wrap() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -336,6 +340,7 @@ fn test_single_swap_strategy_encoder_unwrap() {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
|
||||
@@ -357,7 +362,7 @@ fn test_single_swap_strategy_encoder_unwrap() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
|
||||
@@ -42,6 +42,7 @@ fn test_split_swap_strategy_encoder() {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_wbtc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -56,6 +57,7 @@ fn test_split_swap_strategy_encoder() {
|
||||
// It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -67,6 +69,7 @@ fn test_split_swap_strategy_encoder() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -78,6 +81,7 @@ fn test_split_swap_strategy_encoder() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
|
||||
@@ -99,7 +103,7 @@ fn test_split_swap_strategy_encoder() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -144,6 +148,7 @@ fn test_split_input_cyclic_swap() {
|
||||
token_out: weth.clone(),
|
||||
split: 0.6f64, // 60% of input
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// USDC -> WETH (Pool 2) - 40% of input (remaining)
|
||||
@@ -166,6 +171,7 @@ fn test_split_input_cyclic_swap() {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None, // Remaining 40%
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// WETH -> USDC (Pool 2)
|
||||
@@ -188,6 +194,7 @@ fn test_split_input_cyclic_swap() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
@@ -212,7 +219,7 @@ fn test_split_input_cyclic_swap() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
@@ -308,6 +315,7 @@ fn test_split_output_cyclic_swap() {
|
||||
token_out: weth.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_weth_usdc_v3_pool1 = Swap {
|
||||
@@ -327,6 +335,7 @@ fn test_split_output_cyclic_swap() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.6f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_weth_usdc_v3_pool2 = Swap {
|
||||
@@ -348,6 +357,7 @@ fn test_split_output_cyclic_swap() {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2);
|
||||
@@ -372,7 +382,7 @@ fn test_split_output_cyclic_swap() {
|
||||
.clone();
|
||||
|
||||
let calldata = encode_tycho_router_call(
|
||||
eth_chain().id,
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFromPermit2,
|
||||
|
||||
125
tests/uniswap_x_integration_tests.rs
Normal file
125
tests/uniswap_x_integration_tests.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use alloy::{hex::encode, primitives::Address, sol_types::SolValue};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
||||
use tycho_execution::encoding::{
|
||||
evm::{
|
||||
approvals::protocol_approvals_manager::ProtocolApprovalsManager,
|
||||
utils::{bytes_to_address, write_calldata_to_file},
|
||||
},
|
||||
models::{Solution, Swap, UserTransferType},
|
||||
};
|
||||
|
||||
use crate::common::{
|
||||
dai, encoding::encode_tycho_router_call, eth, eth_chain, get_tycho_router_encoder,
|
||||
router_address, usdc, usdt,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
fn test_sequential_swap_usx() {
|
||||
// Replicates real uniswap X order settled in tx:
|
||||
// 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d
|
||||
// Performs a sequential
|
||||
// swap from DAI to USDT though USDC using USV3 pools
|
||||
//
|
||||
// DAI ───(USV3)──> USDC ───(USV2)──> USDT
|
||||
// Creates all the calldata needed for the uniswap X callbackData
|
||||
|
||||
let filler = Bytes::from_str("0x6D9da78B6A5BEdcA287AA5d49613bA36b90c15C4").unwrap();
|
||||
let usx_reactor = Address::from_str("0x00000011F84B9aa48e5f8aA8B9897600006289Be").unwrap();
|
||||
|
||||
let dai = dai();
|
||||
let usdc = usdc();
|
||||
let usdt = usdt();
|
||||
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168".to_string(),
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
static_attributes: {
|
||||
let mut attrs = HashMap::new();
|
||||
attrs
|
||||
.insert("fee".to_string(), Bytes::from(BigInt::from(100).to_signed_bytes_be()));
|
||||
attrs
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_usdc_usdt = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0x3416cF6C708Da44DB2624D63ea0AAef7113527C6".to_string(),
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
static_attributes: {
|
||||
let mut attrs = HashMap::new();
|
||||
attrs
|
||||
.insert("fee".to_string(), Bytes::from(BigInt::from(100).to_signed_bytes_be()));
|
||||
attrs
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: usdt.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
given_token: dai.clone(),
|
||||
given_amount: BigUint::from_str("2_000_000000000000000000").unwrap(),
|
||||
checked_token: usdt.clone(),
|
||||
checked_amount: BigUint::from_str("1_990_000000").unwrap(),
|
||||
sender: filler.clone(),
|
||||
receiver: filler.clone(),
|
||||
swaps: vec![swap_dai_usdc, swap_usdc_usdt],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let encoded_solution = encoder
|
||||
.encode_solutions(vec![solution.clone()])
|
||||
.unwrap()[0]
|
||||
.clone();
|
||||
|
||||
let tycho_calldata = encode_tycho_router_call(
|
||||
eth_chain().id(),
|
||||
encoded_solution,
|
||||
&solution,
|
||||
&UserTransferType::TransferFrom,
|
||||
ð(),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.data;
|
||||
|
||||
// Uniswap X specific part
|
||||
let filler_address = bytes_to_address(&filler).unwrap();
|
||||
let token_approvals_manager = ProtocolApprovalsManager::new().unwrap();
|
||||
|
||||
let token_in_approval_needed = token_approvals_manager
|
||||
.approval_needed(
|
||||
bytes_to_address(&dai).unwrap(),
|
||||
filler_address,
|
||||
bytes_to_address(&router_address()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let token_out_approval_needed = token_approvals_manager
|
||||
.approval_needed(bytes_to_address(&usdc).unwrap(), filler_address, usx_reactor)
|
||||
.unwrap();
|
||||
|
||||
let full_calldata =
|
||||
(token_in_approval_needed, token_out_approval_needed, tycho_calldata).abi_encode_packed();
|
||||
|
||||
let hex_calldata = encode(&full_calldata);
|
||||
write_calldata_to_file("test_sequential_swap_usx", hex_calldata.as_str());
|
||||
}
|
||||
Reference in New Issue
Block a user