- This way this chain object contains everything we need, we don't need to worry about doing any transformation or calling any supplementary functions inside any of the encoders - Needed to move our new Chain object to a higher level since this is used in the higher-level encoder traits. This required some weird default values in the constants in order to avoid using alloy's hex literal. I could have instead opted to make Bytes parse a string I think, though this would mean possibly returning an error at the constants level, which is not nice either. Question: - Do we want the user to be in charge of passing the native and wrapped token every single time? This may be a bit annoying for the user. For now, I have defaulted to those in constants.rs, this would take 5 mins to remove though if you don't like it, and it would get rid of this complicated bytes initialization.
160 lines
6.1 KiB
Rust
160 lines
6.1 KiB
Rust
use std::str::FromStr;
|
|
|
|
use num_bigint::BigUint;
|
|
use tycho_core::{
|
|
dto::{Chain as TychoCoreChain, ProtocolComponent},
|
|
Bytes,
|
|
};
|
|
use tycho_execution::encoding::{
|
|
evm::{
|
|
strategy_encoder::strategy_encoder_registry::EVMStrategyEncoderRegistry,
|
|
tycho_encoder::EVMTychoEncoder,
|
|
},
|
|
models::{Chain, Solution, Swap},
|
|
strategy_encoder::StrategyEncoderRegistry,
|
|
tycho_encoder::TychoEncoder,
|
|
};
|
|
|
|
fn main() {
|
|
// Setup variables
|
|
let router_address = "0x1234567890abcdef1234567890abcdef12345678".to_string();
|
|
let signer_pk =
|
|
Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string());
|
|
let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2")
|
|
.expect("Failed to create user address");
|
|
let executors_file_path = "src/encoding/config/executor_addresses.json";
|
|
|
|
let eth_chain = Chain::from_tycho_core_chain(TychoCoreChain::Ethereum, None, None)
|
|
.expect("Failed to create chain.");
|
|
// Initialize the encoder
|
|
let strategy_encoder_registry =
|
|
EVMStrategyEncoderRegistry::new(eth_chain.clone(), executors_file_path, signer_pk.clone())
|
|
.expect("Failed to create strategy encoder registry");
|
|
let encoder = EVMTychoEncoder::new(strategy_encoder_registry, router_address, eth_chain)
|
|
.expect("Failed to create encoder");
|
|
|
|
// ------------------- Encode a simple swap -------------------
|
|
|
|
// Prepare data to encode. We will encode a simple swap from WETH to USDC
|
|
// First we need to create a swap object
|
|
let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")
|
|
.expect("Failed to create WETH address");
|
|
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
|
|
.expect("Failed to create USDC address");
|
|
|
|
let simple_swap = Swap {
|
|
// The protocol component data comes from tycho-indexer
|
|
component: ProtocolComponent {
|
|
id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(),
|
|
protocol_system: "uniswap_v2".to_string(),
|
|
..Default::default()
|
|
},
|
|
token_in: weth.clone(),
|
|
token_out: usdc.clone(),
|
|
// Split defines the fraction of the amount to be swapped. A value of 0 indicates 100% of
|
|
// the amount or the total remaining balance.
|
|
split: 0f64,
|
|
};
|
|
|
|
// Then we create a solution object with the previous swap
|
|
let solution = Solution {
|
|
sender: user_address.clone(),
|
|
receiver: user_address.clone(),
|
|
given_token: weth.clone(),
|
|
given_amount: BigUint::from_str("1_000000000000000000").expect("Failed to create amount"),
|
|
checked_token: usdc.clone(),
|
|
exact_out: false, // it's an exact in solution
|
|
check_amount: None, // the amount out will not be checked in execution
|
|
swaps: vec![simple_swap],
|
|
..Default::default()
|
|
};
|
|
|
|
// Encode the solution
|
|
let tx = encoder
|
|
.encode_router_calldata(vec![solution.clone()])
|
|
.expect("Failed to encode router calldata")[0]
|
|
.clone();
|
|
println!(" ====== Simple swap WETH -> USDC ======");
|
|
println!(
|
|
"The simple swap encoded transaction should be sent to address {:?} with the value of {:?} and the \
|
|
following encoded data: {:?}",
|
|
tx.to,
|
|
tx.value,
|
|
hex::encode(tx.data)
|
|
);
|
|
|
|
// ------------------- Encode a swap with multiple splits -------------------
|
|
// To illustrate a more complex solution, we will encode a swap from WETH to USDC with multiple
|
|
// splits. Performs a split swap from WETH to USDC though WBTC and DAI using USV2 pools
|
|
//
|
|
// ┌──(USV2)──> WBTC ───(USV2)──> USDC
|
|
// WETH ─┤
|
|
// └──(USV2)──> DAI ───(USV2)──> USDC
|
|
//
|
|
|
|
let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599")
|
|
.expect("Failed to create WBTC address");
|
|
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f")
|
|
.expect("Failed to create DAI address");
|
|
|
|
let swap_weth_dai = Swap {
|
|
component: ProtocolComponent {
|
|
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
|
protocol_system: "uniswap_v2".to_string(),
|
|
..Default::default()
|
|
},
|
|
token_in: weth.clone(),
|
|
token_out: dai.clone(),
|
|
split: 0.5f64,
|
|
};
|
|
let swap_weth_wbtc = Swap {
|
|
component: ProtocolComponent {
|
|
id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
|
|
protocol_system: "uniswap_v2".to_string(),
|
|
..Default::default()
|
|
},
|
|
token_in: weth.clone(),
|
|
token_out: wbtc.clone(),
|
|
// This represents the remaining 50%, but to avoid any rounding errors we set this to
|
|
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
|
split: 0f64,
|
|
};
|
|
let swap_dai_usdc = Swap {
|
|
component: ProtocolComponent {
|
|
id: "0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5".to_string(),
|
|
protocol_system: "uniswap_v2".to_string(),
|
|
..Default::default()
|
|
},
|
|
token_in: dai.clone(),
|
|
token_out: usdc.clone(),
|
|
split: 0f64,
|
|
};
|
|
let swap_wbtc_usdc = Swap {
|
|
component: ProtocolComponent {
|
|
id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
|
|
protocol_system: "uniswap_v2".to_string(),
|
|
..Default::default()
|
|
},
|
|
token_in: wbtc.clone(),
|
|
token_out: usdc.clone(),
|
|
split: 0f64,
|
|
};
|
|
let mut complex_solution = solution.clone();
|
|
complex_solution.swaps = vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc];
|
|
|
|
// Encode the solution
|
|
let complex_tx = encoder
|
|
.encode_router_calldata(vec![complex_solution])
|
|
.expect("Failed to encode router calldata")[0]
|
|
.clone();
|
|
|
|
println!(" ====== Complex split swap WETH -> USDC ======");
|
|
println!(
|
|
"The complex solution encoded transaction should be sent to address {:?} with the value of {:?} and the \
|
|
following encoded data: {:?}",
|
|
complex_tx.to,
|
|
complex_tx.value,
|
|
hex::encode(complex_tx.data)
|
|
);
|
|
}
|