feat: Add transferFromNeeded to non permit2 methods

This will block an attempt to transfer from the user when we expect the funds to already be in the router.

chores:
- add docs
- in EncodingContext, rename transfer to transfer_type

Took 58 minutes
This commit is contained in:
Diana Carvalho
2025-05-19 12:02:57 +01:00
parent e0c195f63d
commit cc9e88cfed
13 changed files with 179 additions and 76 deletions

View File

@@ -33,6 +33,7 @@ use crate::encoding::{
/// * `selector`: String, the selector 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
/// * `token_in_already_in_router`: bool, whether the token in is already in the router
#[derive(Clone)]
pub struct SingleSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
@@ -40,6 +41,7 @@ pub struct SingleSwapStrategyEncoder {
selector: String,
router_address: Bytes,
transfer_optimization: TransferOptimization,
token_in_already_in_router: bool,
}
impl SingleSwapStrategyEncoder {
@@ -55,7 +57,8 @@ impl SingleSwapStrategyEncoder {
} else {
(
None,
"singleSwap(uint256,address,address,uint256,bool,bool,address,bytes)".to_string(),
"singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
.to_string(),
)
};
Ok(Self {
@@ -69,6 +72,7 @@ impl SingleSwapStrategyEncoder {
token_in_already_in_router,
router_address,
),
token_in_already_in_router,
})
}
@@ -132,7 +136,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer,
transfer_type: transfer,
};
let mut grouped_protocol_data: Vec<u8> = vec![];
@@ -176,6 +180,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
wrap,
unwrap,
bytes_to_address(&solution.receiver)?,
!self.token_in_already_in_router,
swap_data,
)
.abi_encode()
@@ -208,6 +213,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
/// * `sequential_swap_validator`: SequentialSwapValidator, responsible for checking validity of
/// sequential swap solutions
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
/// * `token_in_already_in_router`: bool, whether the token in is already in the router
#[derive(Clone)]
pub struct SequentialSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
@@ -218,6 +224,7 @@ pub struct SequentialSwapStrategyEncoder {
wrapped_address: Bytes,
sequential_swap_validator: SequentialSwapValidator,
transfer_optimization: TransferOptimization,
token_in_already_in_router: bool,
}
impl SequentialSwapStrategyEncoder {
@@ -233,7 +240,7 @@ impl SequentialSwapStrategyEncoder {
} else {
(
None,
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bytes)"
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
.to_string(),
)
};
@@ -251,6 +258,7 @@ impl SequentialSwapStrategyEncoder {
token_in_already_in_router,
router_address,
),
token_in_already_in_router,
})
}
@@ -322,7 +330,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer,
transfer_type: transfer,
};
let mut grouped_protocol_data: Vec<u8> = vec![];
@@ -371,6 +379,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
wrap,
unwrap,
bytes_to_address(&solution.receiver)?,
!self.token_in_already_in_router,
encoded_swaps,
)
.abi_encode()
@@ -403,6 +412,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
/// solutions
/// * `router_address`: Address of the router to be used to execute swaps
/// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers
/// * `token_in_already_in_router`: bool, whether the token in is already in the router
#[derive(Clone)]
pub struct SplitSwapStrategyEncoder {
swap_encoder_registry: SwapEncoderRegistry,
@@ -413,6 +423,7 @@ pub struct SplitSwapStrategyEncoder {
split_swap_validator: SplitSwapValidator,
router_address: Bytes,
transfer_optimization: TransferOptimization,
token_in_already_in_router: bool,
}
impl SplitSwapStrategyEncoder {
@@ -428,7 +439,7 @@ impl SplitSwapStrategyEncoder {
} else {
(
None,
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bytes)"
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
.to_string(),
)
};
@@ -446,6 +457,7 @@ impl SplitSwapStrategyEncoder {
token_in_already_in_router,
router_address,
),
token_in_already_in_router,
})
}
@@ -557,7 +569,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer,
transfer_type: transfer,
};
let mut grouped_protocol_data: Vec<u8> = vec![];
@@ -616,6 +628,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
unwrap,
U256::from(tokens_len),
bytes_to_address(&solution.receiver)?,
!self.token_in_already_in_router,
encoded_swaps,
)
.abi_encode()
@@ -834,7 +847,7 @@ mod tests {
.unwrap();
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
let expected_input = [
"20144a07", // Function selector
"5c4b639c", // Function selector
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
@@ -842,7 +855,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000001", // transfer from needed
"0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
// Swap data
@@ -916,7 +930,7 @@ mod tests {
.unwrap();
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
let expected_input = [
"20144a07", // Function selector
"5c4b639c", // Function selector
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
@@ -924,7 +938,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000000", // transfer from not needed
"0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
// Swap data
@@ -1186,7 +1201,7 @@ mod tests {
let hex_calldata = encode(&calldata);
let expected = String::from(concat!(
"e8a980d7", /* function selector */
"e21dd0d3", /* function selector */
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou
@@ -1194,7 +1209,8 @@ mod tests {
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", /* length ple
"0000000000000000000000000000000000000000000000000000000000000001", /* transfer from needed */
"0000000000000000000000000000000000000000000000000000000000000120", /* length ple
* encode */
"00000000000000000000000000000000000000000000000000000000000000a8",
// swap 1
@@ -2693,7 +2709,6 @@ mod tests {
.unwrap();
let hex_calldata = encode(&calldata);
println!("calldata: {}", hex_calldata);
write_calldata_to_file("test_single_encoding_strategy_curve", hex_calldata.as_str());
}

View File

@@ -35,7 +35,7 @@ impl TransferOptimization {
}
}
/// Returns the transfer type that should be used for the first transfer.
/// Returns the transfer type that should be used for the current transfer.
pub fn get_transfers(
&self,
swap: SwapGroup,

View File

@@ -66,7 +66,7 @@ impl SwapEncoder for UniswapV2SwapEncoder {
component_id,
bytes_to_address(&encoding_context.receiver)?,
zero_to_one,
(encoding_context.transfer as u8).to_be_bytes(),
(encoding_context.transfer_type as u8).to_be_bytes(),
);
Ok(args.abi_encode_packed())
@@ -129,7 +129,7 @@ impl SwapEncoder for UniswapV3SwapEncoder {
bytes_to_address(&encoding_context.receiver)?,
component_id,
zero_to_one,
(encoding_context.transfer as u8).to_be_bytes(),
(encoding_context.transfer_type as u8).to_be_bytes(),
);
Ok(args.abi_encode_packed())
@@ -206,7 +206,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
group_token_in_address,
group_token_out_address,
zero_to_one,
(encoding_context.transfer as u8).to_be_bytes(),
(encoding_context.transfer_type as u8).to_be_bytes(),
bytes_to_address(&encoding_context.receiver)?,
pool_params,
);
@@ -282,7 +282,7 @@ impl SwapEncoder for BalancerV2SwapEncoder {
component_id,
bytes_to_address(&encoding_context.receiver)?,
approval_needed,
(encoding_context.transfer as u8).to_be_bytes(),
(encoding_context.transfer_type as u8).to_be_bytes(),
);
Ok(args.abi_encode_packed())
}
@@ -344,7 +344,7 @@ impl SwapEncoder for EkuboSwapEncoder {
let mut encoded = vec![];
if encoding_context.group_token_in == swap.token_in {
encoded.extend((encoding_context.transfer as u8).to_be_bytes());
encoded.extend((encoding_context.transfer_type as u8).to_be_bytes());
encoded.extend(bytes_to_address(&encoding_context.receiver)?);
encoded.extend(bytes_to_address(&swap.token_in)?);
}
@@ -575,7 +575,7 @@ impl SwapEncoder for CurveSwapEncoder {
i.to_be_bytes::<1>(),
j.to_be_bytes::<1>(),
approval_needed,
(encoding_context.transfer as u8).to_be_bytes(),
(encoding_context.transfer_type as u8).to_be_bytes(),
bytes_to_address(&encoding_context.receiver)?,
);
@@ -620,7 +620,7 @@ impl SwapEncoder for MaverickV2SwapEncoder {
bytes_to_address(&swap.token_in)?,
component_id,
bytes_to_address(&encoding_context.receiver)?,
(encoding_context.transfer as u8).to_be_bytes(),
(encoding_context.transfer_type as u8).to_be_bytes(),
);
Ok(args.abi_encode_packed())
}
@@ -670,7 +670,7 @@ mod tests {
router_address: Some(Bytes::zero(20)),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV2SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
@@ -729,7 +729,7 @@ mod tests {
router_address: Some(Bytes::zero(20)),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV3SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
@@ -791,7 +791,7 @@ mod tests {
router_address: Some(Bytes::zero(20)),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::None,
transfer_type: TransferType::None,
};
let encoder = BalancerV2SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
@@ -865,7 +865,7 @@ mod tests {
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV4SwapEncoder::new(
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
@@ -936,7 +936,7 @@ mod tests {
group_token_in: group_token_in.clone(),
// Token out is the same as the group token out
group_token_out: token_out.clone(),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let encoder = UniswapV4SwapEncoder::new(
@@ -979,7 +979,7 @@ mod tests {
router_address: Some(router_address.clone()),
group_token_in: usde_address.clone(),
group_token_out: wbtc_address.clone(),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
// Setup - First sequence: USDE -> USDT
@@ -1114,7 +1114,7 @@ mod tests {
group_token_out: token_out.clone(),
exact_out: false,
router_address: Some(Bytes::default()),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let encoder =
@@ -1160,7 +1160,7 @@ mod tests {
group_token_out: group_token_out.clone(),
exact_out: false,
router_address: Some(Bytes::default()),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let first_swap = Swap {
@@ -1356,7 +1356,7 @@ mod tests {
router_address: None,
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::None,
transfer_type: TransferType::None,
};
let encoder = CurveSwapEncoder::new(
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
@@ -1426,7 +1426,7 @@ mod tests {
router_address: None,
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::None,
transfer_type: TransferType::None,
};
let encoder = CurveSwapEncoder::new(
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
@@ -1497,7 +1497,7 @@ mod tests {
router_address: None,
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::None,
transfer_type: TransferType::None,
};
let encoder = CurveSwapEncoder::new(
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
@@ -1568,7 +1568,7 @@ mod tests {
router_address: Some(Bytes::default()),
group_token_in: token_in.clone(),
group_token_out: token_out.clone(),
transfer: TransferType::Transfer,
transfer_type: TransferType::Transfer,
};
let encoder = MaverickV2SwapEncoder::new(
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),

View File

@@ -286,7 +286,7 @@ impl TychoExecutorEncoder {
router_address: None,
group_token_in: grouped_swap.token_in.clone(),
group_token_out: grouped_swap.token_out.clone(),
transfer,
transfer_type: transfer,
};
let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?;
grouped_protocol_data.extend(protocol_data);
@@ -468,7 +468,7 @@ mod tests {
Bytes::from_str("0x3ede3eca2a72b3aecc820e955b36f38437d01395").unwrap()
);
// single swap selector
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "20144a07");
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "5c4b639c");
}
#[test]
@@ -493,7 +493,7 @@ mod tests {
let transactions = transactions.unwrap();
assert_eq!(transactions.len(), 1);
// single swap selector
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "20144a07");
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "5c4b639c");
}
#[test]
@@ -540,7 +540,7 @@ mod tests {
assert_eq!(transactions.len(), 1);
assert_eq!(transactions[0].value, eth_amount_in);
// sequential swap selector
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "e8a980d7");
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "e21dd0d3");
}
#[test]

View File

@@ -114,15 +114,15 @@ pub struct EncodingContext {
pub router_address: Option<Bytes>,
pub group_token_in: Bytes,
pub group_token_out: Bytes,
pub transfer: TransferType,
pub transfer_type: TransferType,
}
/// Represents the type of transfer to be performed into the pool.
///
/// # Fields
///
/// * `Transfer`: Transfer the token from the router into the protocol.
/// * `TransferFrom`: Transfer the token from the sender to the protocol/router.
/// * `Transfer`: Transfer the token from the router into the protocol.
/// * `None`: No transfer is needed. Tokens are already in the pool.
#[repr(u8)]
#[derive(Clone, Debug, PartialEq)]