feat: add util fns, change callback_selector to string, update first_swap check
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use alloy_primitives::{keccak256, Address, Bytes as AlloyBytes};
|
use alloy_primitives::{Address, Bytes as AlloyBytes};
|
||||||
use alloy_sol_types::SolValue;
|
use alloy_sol_types::SolValue;
|
||||||
|
|
||||||
use crate::encoding::{
|
use crate::encoding::{
|
||||||
errors::EncodingError,
|
errors::EncodingError,
|
||||||
evm::{
|
evm::{
|
||||||
approvals::protocol_approvals_manager::ProtocolApprovalsManager, utils::bytes_to_address,
|
approvals::protocol_approvals_manager::ProtocolApprovalsManager,
|
||||||
|
utils::{bytes_to_address, encode_function_selector, pad_to_fixed_size},
|
||||||
},
|
},
|
||||||
models::{EncodingContext, Swap},
|
models::{EncodingContext, Swap},
|
||||||
swap_encoder::SwapEncoder,
|
swap_encoder::SwapEncoder,
|
||||||
@@ -115,13 +116,7 @@ impl SwapEncoder for UniswapV3SwapEncoder {
|
|||||||
})?
|
})?
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
// this is necessary to pad on the left with zeros if the fee is less than 3 bytes
|
let pool_fee_u24 = pad_to_fixed_size::<3>(&pool_fee_bytes)
|
||||||
let mut padded_fee_bytes = [0u8; 3];
|
|
||||||
let start = 3 - pool_fee_bytes.len();
|
|
||||||
padded_fee_bytes[start..].copy_from_slice(&pool_fee_bytes);
|
|
||||||
|
|
||||||
let pool_fee_u24: [u8; 3] = padded_fee_bytes[(padded_fee_bytes.len() - 3)..]
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| EncodingError::FatalError("Failed to extract fee bytes".to_string()))?;
|
.map_err(|_| EncodingError::FatalError("Failed to extract fee bytes".to_string()))?;
|
||||||
|
|
||||||
let args = (
|
let args = (
|
||||||
@@ -152,13 +147,12 @@ impl SwapEncoder for UniswapV3SwapEncoder {
|
|||||||
/// # Fields
|
/// # Fields
|
||||||
/// * `executor_address` - The address of the executor contract that will perform the swap.
|
/// * `executor_address` - The address of the executor contract that will perform the swap.
|
||||||
/// * `executor_selector` - The selector of the swap function in the executor contract.
|
/// * `executor_selector` - The selector of the swap function in the executor contract.
|
||||||
/// * `callback_selector` - The pre-computed selector of the callback function in the executor
|
/// * `callback_selector` - The selector of the callback function in the executor contract.
|
||||||
/// contract.
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UniswapV4SwapEncoder {
|
pub struct UniswapV4SwapEncoder {
|
||||||
executor_address: String,
|
executor_address: String,
|
||||||
executor_selector: String,
|
executor_selector: String,
|
||||||
callback_selector: [u8; 4],
|
callback_selector: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UniswapV4SwapEncoder {
|
impl UniswapV4SwapEncoder {
|
||||||
@@ -171,29 +165,16 @@ impl UniswapV4SwapEncoder {
|
|||||||
fee: [u8; 3],
|
fee: [u8; 3],
|
||||||
tick_spacing: [u8; 3],
|
tick_spacing: [u8; 3],
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
let mut encoded = Vec::with_capacity(26);
|
(intermediary_token, fee, tick_spacing).abi_encode_packed()
|
||||||
// Encode intermediary token (20 bytes)
|
|
||||||
encoded.extend_from_slice(intermediary_token.as_ref());
|
|
||||||
// Encode fee (3 bytes)
|
|
||||||
encoded.extend_from_slice(fee.as_ref());
|
|
||||||
// Encode tick spacing (3 bytes)
|
|
||||||
encoded.extend_from_slice(tick_spacing.as_ref());
|
|
||||||
encoded
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SwapEncoder for UniswapV4SwapEncoder {
|
impl SwapEncoder for UniswapV4SwapEncoder {
|
||||||
fn new(executor_address: String) -> Self {
|
fn new(executor_address: String) -> Self {
|
||||||
// Pre-compute the callback selector for "unlockCallback(bytes)"
|
|
||||||
// This matches how Solidity computes function selectors
|
|
||||||
let callback_selector = keccak256(b"unlockCallback(bytes)")[..4]
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
executor_address,
|
executor_address,
|
||||||
executor_selector: "swap(uint256,bytes)".to_string(),
|
executor_selector: "swap(uint256,bytes)".to_string(),
|
||||||
callback_selector,
|
callback_selector: "unlockCallback(bytes)".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,19 +184,16 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
encoding_context: EncodingContext,
|
encoding_context: EncodingContext,
|
||||||
) -> Result<Vec<u8>, EncodingError> {
|
) -> Result<Vec<u8>, EncodingError> {
|
||||||
let mut first_swap = false;
|
let mut first_swap = false;
|
||||||
if encoding_context.group_token_in == Some(swap.token_in.clone()) &&
|
if encoding_context.group_token_in == Some(swap.token_in.clone()) {
|
||||||
encoding_context.group_token_out == Some(swap.token_out.clone())
|
|
||||||
{
|
|
||||||
first_swap = true;
|
first_swap = true;
|
||||||
}
|
}
|
||||||
let token_in_address = bytes_to_address(&swap.token_in)?;
|
let token_in_address = bytes_to_address(&swap.token_in)?;
|
||||||
let token_out_address = bytes_to_address(&swap.token_out)?;
|
let token_out_address = bytes_to_address(&swap.token_out)?;
|
||||||
let mut amount_out_min = vec![0u8; 32]; // Create a zero-filled buffer of 32 bytes
|
let mut amount_out_min = vec![0u8; 32]; // Zero-filled buffer of 32 bytes
|
||||||
let min_value = encoding_context
|
let min_value = encoding_context
|
||||||
.amount_out_min
|
.amount_out_min
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_bytes_be();
|
.to_bytes_be();
|
||||||
// Copy the actual value to the end of the buffer, maintaining leading zeros
|
|
||||||
amount_out_min[(32 - min_value.len())..].copy_from_slice(&min_value);
|
amount_out_min[(32 - min_value.len())..].copy_from_slice(&min_value);
|
||||||
let zero_to_one = Self::get_zero_to_one(token_in_address, token_out_address);
|
let zero_to_one = Self::get_zero_to_one(token_in_address, token_out_address);
|
||||||
let callback_executor = bytes_to_address(&encoding_context.router_address)?;
|
let callback_executor = bytes_to_address(&encoding_context.router_address)?;
|
||||||
@@ -231,13 +209,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
})?
|
})?
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
// Pad on the left with zeros if the fee is less than 3 bytes
|
let pool_fee_u24 = pad_to_fixed_size::<3>(&fee)
|
||||||
let mut padded_fee_bytes = [0u8; 3];
|
|
||||||
let start = 3 - fee.len();
|
|
||||||
padded_fee_bytes[start..].copy_from_slice(&fee);
|
|
||||||
|
|
||||||
let pool_fee_u24: [u8; 3] = padded_fee_bytes[(padded_fee_bytes.len() - 3)..]
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| EncodingError::FatalError("Failed to extract fee bytes".to_string()))?;
|
.map_err(|_| EncodingError::FatalError("Failed to extract fee bytes".to_string()))?;
|
||||||
|
|
||||||
let tick_spacing = swap
|
let tick_spacing = swap
|
||||||
@@ -251,15 +223,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
})?
|
})?
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
// Pad on the left with zeros if the tick spacing is less than 3 bytes
|
let pool_tick_spacing_u24 = pad_to_fixed_size::<3>(&tick_spacing).map_err(|_| {
|
||||||
let mut padded_tick_spacing_bytes = [0u8; 3];
|
|
||||||
let start = 3 - tick_spacing.len();
|
|
||||||
padded_tick_spacing_bytes[start..].copy_from_slice(&tick_spacing);
|
|
||||||
|
|
||||||
let pool_tick_spacing_u24: [u8; 3] = padded_tick_spacing_bytes
|
|
||||||
[(padded_tick_spacing_bytes.len() - 3)..]
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| {
|
|
||||||
EncodingError::FatalError("Failed to extract tick spacing bytes".to_string())
|
EncodingError::FatalError("Failed to extract tick spacing bytes".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -268,7 +232,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
|
|
||||||
if !first_swap {
|
if !first_swap {
|
||||||
return Ok(Self::encode_pool_params(
|
return Ok(Self::encode_pool_params(
|
||||||
token_in_address,
|
token_out_address,
|
||||||
pool_fee_u24,
|
pool_fee_u24,
|
||||||
pool_tick_spacing_u24,
|
pool_tick_spacing_u24,
|
||||||
));
|
));
|
||||||
@@ -280,7 +244,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
|||||||
amount_out_min,
|
amount_out_min,
|
||||||
zero_to_one,
|
zero_to_one,
|
||||||
callback_executor,
|
callback_executor,
|
||||||
self.callback_selector,
|
encode_function_selector(&self.callback_selector),
|
||||||
pool_params,
|
pool_params,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -624,7 +588,7 @@ mod tests {
|
|||||||
String::from(concat!(
|
String::from(concat!(
|
||||||
// pool params:
|
// pool params:
|
||||||
// - intermediary token (20 bytes)
|
// - intermediary token (20 bytes)
|
||||||
"4c9edd5852cd905f086c759e8383e09bff1e68b3",
|
"dac17f958d2ee523a2206206994597c13d831ec7",
|
||||||
// - fee (3 bytes)
|
// - fee (3 bytes)
|
||||||
"0001f4",
|
"0001f4",
|
||||||
// - tick spacing (3 bytes)
|
// - tick spacing (3 bytes)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
use alloy_primitives::{aliases::U24, Address, Keccak256, U256, U8};
|
use alloy_primitives::{aliases::U24, keccak256, Address, FixedBytes, Keccak256, U256, U8};
|
||||||
use num_bigint::BigUint;
|
use num_bigint::BigUint;
|
||||||
use tycho_core::Bytes;
|
use tycho_core::Bytes;
|
||||||
|
|
||||||
@@ -91,3 +91,17 @@ pub fn get_token_position(tokens: Vec<Bytes>, token: Bytes) -> Result<U8, Encodi
|
|||||||
);
|
);
|
||||||
Ok(position)
|
Ok(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pads a byte slice to a fixed size array with leading zeros.
|
||||||
|
pub fn pad_to_fixed_size<const N: usize>(input: &[u8]) -> Result<[u8; N], EncodingError> {
|
||||||
|
let mut padded = [0u8; N];
|
||||||
|
let start = N - input.len();
|
||||||
|
padded[start..].copy_from_slice(input);
|
||||||
|
Ok(padded)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes a function selector to a fixed size array of 4 bytes.
|
||||||
|
pub fn encode_function_selector(selector: &str) -> FixedBytes<4> {
|
||||||
|
let hash = keccak256(selector.as_bytes());
|
||||||
|
FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]])
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user