Merge pull request #208 from propeller-heads/audit/dc/rename-selector-to-function-signature

chore: Rename selector to function signature
This commit is contained in:
dianacarvalho1
2025-05-27 15:26:20 +01:00
committed by GitHub
11 changed files with 79 additions and 79 deletions

View File

@@ -64,10 +64,10 @@ fn main() {
.clone();
println!(" ====== Simple swap WETH -> USDC ======");
println!(
"The simple swap encoded solution should be sent to address {:?} and selector {:?} and the \
following encoded data: {:?}",
"The simple swap encoded solution should be sent to address {:?} with function signature {:?} and the \
following encoded swaps: {:?}",
encoded_solution.interacting_with,
encoded_solution.selector,
encoded_solution.function_signature,
hex::encode(encoded_solution.swaps)
);
@@ -138,10 +138,10 @@ fn main() {
println!(" ====== Complex split swap WETH -> USDC ======");
println!(
"The complex swaps encoded solution should be sent to address {:?} and selector {:?} and the \
following encoded data: {:?}",
complex_encoded_solution.interacting_with,
complex_encoded_solution.selector,
hex::encode(complex_encoded_solution.swaps)
"The complex swaps encoded solution should be sent to address {:?} with function signature {:?} and the \
following encoded swaps: {:?}",
complex_encoded_solution.interacting_with,
complex_encoded_solution.function_signature,
hex::encode(complex_encoded_solution.swaps)
);
}

View File

@@ -106,7 +106,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let encoded = serde_json::json!({
"swaps": format!("0x{}", hex::encode(&encoded_solutions[0].swaps)),
"interacting_with": format!("0x{}", hex::encode(&encoded_solutions[0].interacting_with)),
"selector": format!("{}",&encoded_solutions[0].selector),
"function_signature": format!("{}",&encoded_solutions[0].function_signature),
"n_tokens": format!("{}", &encoded_solutions[0].n_tokens),
"permit": match encoded_solutions[0].permit.as_ref() {
Some(permit) => {

View File

@@ -17,7 +17,10 @@ use tycho_common::Bytes;
use crate::encoding::{
errors::EncodingError,
evm::utils::{biguint_to_u256, bytes_to_address, encode_input, get_client, get_runtime},
evm::{
encoding_utils::encode_input,
utils::{biguint_to_u256, bytes_to_address, get_client, get_runtime},
},
models,
};

View File

@@ -14,7 +14,10 @@ use tokio::{
use crate::encoding::{
errors::EncodingError,
evm::utils::{encode_input, get_client, get_runtime},
evm::{
encoding_utils::encode_input,
utils::{get_client, get_runtime},
},
};
/// A manager for checking if an approval is needed for interacting with a certain spender.

View File

@@ -4,7 +4,7 @@ use alloy::{
primitives::U256,
signers::{local::PrivateKeySigner, Signature, SignerSync},
};
use alloy_primitives::Address;
use alloy_primitives::{Address, Keccak256};
use alloy_sol_types::{eip712_domain, SolStruct, SolValue};
use num_bigint::BigUint;
use tycho_common::Bytes;
@@ -13,7 +13,6 @@ use crate::encoding::{
errors::EncodingError,
evm::{
approvals::permit2::PermitSingle,
utils,
utils::{biguint_to_u256, bytes_to_address},
},
models,
@@ -34,7 +33,7 @@ use crate::encoding::{
/// - `splitSwapPermit2`
///
/// The encoding includes handling of native asset wrapping/unwrapping, permit2 support,
/// and proper input argument formatting based on the selector string.
/// and proper input argument formatting based on the function signature string.
///
/// # ⚠️ Important Responsibility Note
///
@@ -60,7 +59,7 @@ use crate::encoding::{
/// funds.
///
/// # Parameters
/// - `encoded_solution`: The solution already encoded by Tycho, including selector and swap path.
/// - `encoded_solution`: The solution already encoded by Tycho.
/// - `solution`: The high-level solution including tokens, amounts, and receiver info.
/// - `token_in_already_in_router`: Whether the input token is already present in the router.
/// - `router_address`: The address of the Tycho Router contract.
@@ -71,8 +70,8 @@ use crate::encoding::{
/// value, data), or an error if the inputs are invalid.
///
/// # Errors
/// - Returns `EncodingError::FatalError` if the selector is unsupported or required fields (e.g.,
/// permit or signature) are missing.
/// - Returns `EncodingError::FatalError` if the function signature is unsupported or required
/// fields (e.g., permit or signature) are missing.
pub fn encode_tycho_router_call(
chain_id: u64,
encoded_solution: EncodedSolution,
@@ -109,7 +108,7 @@ pub fn encode_tycho_router_call(
};
let method_calldata = if encoded_solution
.selector
.function_signature
.contains("singleSwapPermit2")
{
(
@@ -128,7 +127,7 @@ pub fn encode_tycho_router_call(
)
.abi_encode()
} else if encoded_solution
.selector
.function_signature
.contains("singleSwap")
{
(
@@ -144,7 +143,7 @@ pub fn encode_tycho_router_call(
)
.abi_encode()
} else if encoded_solution
.selector
.function_signature
.contains("sequentialSwapPermit2")
{
(
@@ -163,7 +162,7 @@ pub fn encode_tycho_router_call(
)
.abi_encode()
} else if encoded_solution
.selector
.function_signature
.contains("sequentialSwap")
{
(
@@ -179,7 +178,7 @@ pub fn encode_tycho_router_call(
)
.abi_encode()
} else if encoded_solution
.selector
.function_signature
.contains("splitSwapPermit2")
{
(
@@ -199,7 +198,7 @@ pub fn encode_tycho_router_call(
)
.abi_encode()
} else if encoded_solution
.selector
.function_signature
.contains("splitSwap")
{
(
@@ -216,10 +215,10 @@ pub fn encode_tycho_router_call(
)
.abi_encode()
} else {
Err(EncodingError::FatalError("Invalid selector for Tycho router".to_string()))?
Err(EncodingError::FatalError("Invalid function signature for Tycho router".to_string()))?
};
let contract_interaction = utils::encode_input(&encoded_solution.selector, method_calldata);
let contract_interaction = encode_input(&encoded_solution.function_signature, method_calldata);
let value = if solution.given_token == native_address {
solution.given_amount.clone()
} else {
@@ -257,3 +256,25 @@ pub fn sign_permit(
EncodingError::FatalError(format!("Failed to sign permit2 approval with error: {e}"))
})
}
/// 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
}

View File

@@ -23,13 +23,13 @@ use crate::encoding::{
///
/// # Fields
/// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders
/// * `selector`: String, the selector for the swap function in the router contract
/// * `function_signature`: String, the signature for the swap function in the router contract
/// * `router_address`: Address of the router to be used to execute swaps
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
#[derive(Clone)]
pub struct SingleSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
selector: String,
function_signature: String,
router_address: Bytes,
transfer_optimization: TransferOptimization,
}
@@ -41,14 +41,14 @@ impl SingleSwapStrategyEncoder {
user_transfer_type: UserTransferType,
router_address: Bytes,
) -> Result<Self, EncodingError> {
let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 {
let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
"singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
} else {
"singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
}.to_string();
Ok(Self {
selector,
function_signature,
swap_encoder_registry,
router_address: router_address.clone(),
transfer_optimization: TransferOptimization::new(
@@ -133,7 +133,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
grouped_protocol_data,
);
Ok(EncodedSolution {
selector: self.selector.clone(),
function_signature: self.function_signature.clone(),
interacting_with: self.router_address.clone(),
swaps: swap_data,
permit: None,
@@ -155,7 +155,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
///
/// # Fields
/// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders
/// * `selector`: String, the selector for the swap function in the router contract
/// * `function_signature`: String, the signature for the swap function in the router contract
/// * `native_address`: Address of the chain's native token
/// * `wrapped_address`: Address of the chain's wrapped token
/// * `router_address`: Address of the router to be used to execute swaps
@@ -165,7 +165,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
#[derive(Clone)]
pub struct SequentialSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
selector: String,
function_signature: String,
router_address: Bytes,
native_address: Bytes,
wrapped_address: Bytes,
@@ -180,14 +180,14 @@ impl SequentialSwapStrategyEncoder {
user_transfer_type: UserTransferType,
router_address: Bytes,
) -> Result<Self, EncodingError> {
let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 {
let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
"sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
} else {
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
}.to_string();
Ok(Self {
selector,
function_signature,
swap_encoder_registry,
router_address: router_address.clone(),
native_address: chain.native_token()?,
@@ -288,7 +288,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
let encoded_swaps = ple_encode(swaps);
Ok(EncodedSolution {
interacting_with: self.router_address.clone(),
selector: self.selector.clone(),
function_signature: self.function_signature.clone(),
swaps: encoded_swaps,
permit: None,
n_tokens: 0,
@@ -309,7 +309,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
///
/// # Fields
/// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders
/// * `selector`: String, the selector for the swap function in the router contract
/// * `function_signature`: String, the signature for the swap function in the router contract
/// * `native_address`: Address of the chain's native token
/// * `wrapped_address`: Address of the chain's wrapped token
/// * `split_swap_validator`: SplitSwapValidator, responsible for checking validity of split swap
@@ -319,7 +319,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
#[derive(Clone)]
pub struct SplitSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
selector: String,
function_signature: String,
native_address: Bytes,
wrapped_address: Bytes,
split_swap_validator: SplitSwapValidator,
@@ -334,13 +334,13 @@ impl SplitSwapStrategyEncoder {
user_transfer_type: UserTransferType,
router_address: Bytes,
) -> Result<Self, EncodingError> {
let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 {
let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 {
"splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
} else {
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
}.to_string();
Ok(Self {
selector,
function_signature,
swap_encoder_registry,
native_address: chain.native_token()?,
wrapped_address: chain.wrapped_token()?,
@@ -489,7 +489,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
};
Ok(EncodedSolution {
interacting_with: self.router_address.clone(),
selector: self.selector.clone(),
function_signature: self.function_signature.clone(),
swaps: encoded_swaps,
permit: None,
n_tokens: tokens_len,
@@ -596,7 +596,7 @@ mod tests {
let hex_calldata = encode(&encoded_solution.swaps);
assert_eq!(hex_calldata, expected_swap);
assert_eq!(encoded_solution.selector, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string());
assert_eq!(encoded_solution.interacting_with, router_address());
}
@@ -659,7 +659,7 @@ mod tests {
assert_eq!(hex_calldata, expected_input);
assert_eq!(
encoded_solution.selector,
encoded_solution.function_signature,
"singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
.to_string()
);
@@ -747,7 +747,7 @@ mod tests {
assert_eq!(hex_calldata, expected);
assert_eq!(
encoded_solution.selector,
encoded_solution.function_signature,
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
.to_string()
);
@@ -902,7 +902,7 @@ mod tests {
.join("");
assert_eq!(hex_calldata, expected_swaps);
assert_eq!(
encoded_solution.selector,
encoded_solution.function_signature,
"splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)"
.to_string()
);
@@ -1050,7 +1050,7 @@ mod tests {
assert_eq!(hex_calldata, expected_swaps);
assert_eq!(
encoded_solution.selector,
encoded_solution.function_signature,
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
.to_string()
);

View File

@@ -19,7 +19,6 @@ use crate::encoding::{
///
/// # Fields
/// * `executor_address` - The address of the executor contract that will perform the swap.
/// * `swap_selector` - The selector of the swap function in the executor contract.
#[derive(Clone)]
pub struct UniswapV2SwapEncoder {
executor_address: String,
@@ -78,7 +77,6 @@ impl SwapEncoder for UniswapV2SwapEncoder {
///
/// # Fields
/// * `executor_address` - The address of the executor contract that will perform the swap.
/// * `swap_selector` - The selector of the swap function in the executor contract.
#[derive(Clone)]
pub struct UniswapV3SwapEncoder {
executor_address: String,
@@ -140,8 +138,6 @@ impl SwapEncoder for UniswapV3SwapEncoder {
///
/// # Fields
/// * `executor_address` - The address of the executor contract that will perform the swap.
/// * `swap_selector` - The selector of the swap function in the executor contract.
/// * `callback_selector` - The selector of the callback function in the executor contract.
#[derive(Clone)]
pub struct UniswapV4SwapEncoder {
executor_address: String,

View File

@@ -337,7 +337,7 @@ impl TychoExecutorEncoder {
swaps: grouped_protocol_data,
interacting_with: executor_address,
permit: None,
selector: "".to_string(),
function_signature: "".to_string(),
n_tokens: 0,
})
}

View File

@@ -9,7 +9,7 @@ use alloy::{
providers::{ProviderBuilder, RootProvider},
transports::BoxTransport,
};
use alloy_primitives::{aliases::U24, Address, Keccak256, U256, U8};
use alloy_primitives::{aliases::U24, Address, U256, U8};
use alloy_sol_types::SolValue;
use num_bigint::BigUint;
use once_cell::sync::Lazy;
@@ -159,25 +159,3 @@ pub fn write_calldata_to_file(test_identifier: &str, hex_calldata: &str) {
writeln!(file, "{line}").expect("Failed to write calldata");
}
}
/// 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
}

View File

@@ -105,7 +105,6 @@ impl Swap {
/// * `to`: Address of the contract to call with the calldata
/// * `value`: Native token value to be sent with the transaction.
/// * `data`: Encoded calldata for the transaction.
/// * `selector`: Only relevant for direct executions. The selector of the function to be called.
#[derive(Clone, Debug)]
pub struct Transaction {
pub to: Bytes,
@@ -118,14 +117,14 @@ pub struct Transaction {
/// # Fields
/// * `swaps`: Encoded swaps to be executed.
/// * `interacting_with`: Address of the contract to be called.
/// * `selector`: The selector of the function to be called.
/// * `function_signature`: The signature of the function to be called.
/// * `n_tokens`: Number of tokens in the swap.
/// * `permit`: Optional permit for the swap (if permit2 is enabled).
#[derive(Clone, Debug)]
pub struct EncodedSolution {
pub swaps: Vec<u8>,
pub interacting_with: Bytes,
pub selector: String,
pub function_signature: String,
pub n_tokens: usize,
pub permit: Option<PermitSingle>,
}

View File

@@ -26,8 +26,8 @@ use crate::encoding::{
/// outer function call arguments themselves** and verify that they enforce correct and secure
/// behavior.
pub trait TychoEncoder {
/// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the selector and
/// internal swap call data.
/// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the function
/// signature and internal swap call data.
///
/// This method gives users maximum flexibility and control. It **does not** produce full
/// transaction objects. Users are responsible for:
@@ -36,7 +36,7 @@ pub trait TychoEncoder {
///
/// # Returns
/// A vector of encoded solutions, each containing:
/// - The Tycho method selector
/// - The Tycho method function signature
/// - The encoded swap path
/// - Additional metadata (e.g., permit2 information)
///