Merge pull request #46 from propeller-heads/encoding/tnl/fix-wrapping-indices
fix: Fix bug with token indexing when wrapping/unwrapping
This commit is contained in:
@@ -208,6 +208,7 @@ contract TychoRouter is
|
||||
tokenInIndex = swapData.tokenInIndex();
|
||||
tokenOutIndex = swapData.tokenOutIndex();
|
||||
split = swapData.splitPercentage();
|
||||
|
||||
currentAmountIn = split > 0
|
||||
? (amounts[tokenInIndex] * split) / 0xffffff
|
||||
: remainingAmounts[tokenInIndex];
|
||||
|
||||
@@ -699,6 +699,64 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
||||
assertGt(balancerAfter - balancerBefore, 26173932);
|
||||
}
|
||||
|
||||
function testSingleSwapWithWrapIntegration() public {
|
||||
// Test created with calldata from our router encoder, replacing the executor
|
||||
// address with the USV2 executor address.
|
||||
|
||||
// Tests swapping WETH -> DAI on a USV2 pool, but ETH is received from the user
|
||||
// and wrapped before the swap
|
||||
deal(ALICE, 1 ether);
|
||||
uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max);
|
||||
// Encoded solution generated using
|
||||
// `test_split_swap_strategy_encoder_simple_route_wrap`
|
||||
// but manually replacing the executor address
|
||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
||||
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
||||
(bool success,) = tychoRouterAddr.call{value: 1 ether}(
|
||||
hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c9179300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067a1919b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041cea77a63613f6a02aaee522c91f9569b8377a7f0200d141fafa3e1c42011e1c668555b49a1e7dd960091d0e33764ad24db6550bc761e228864495b478f1a23721b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000"
|
||||
);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balancerAfter = IERC20(DAI_ADDR).balanceOf(ALICE);
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGt(balancerAfter - balancerBefore, 26173932);
|
||||
}
|
||||
|
||||
function testSingleSwapWithUnwrapIntegration() public {
|
||||
// Test created with calldata from our router encoder, replacing the executor
|
||||
// address with the USV2 executor address.
|
||||
|
||||
// Tests swapping DAI -> WETH on a USV2 pool, and WETH is unwrapped to ETH
|
||||
// before sending back to the user
|
||||
deal(DAI_ADDR, ALICE, 3000 ether);
|
||||
uint256 balancerBefore = ALICE.balance;
|
||||
|
||||
// Approve permit2
|
||||
vm.startPrank(ALICE);
|
||||
IERC20(DAI_ADDR).approve(address(permit2Address), type(uint256).max);
|
||||
// Encoded solution generated using
|
||||
// `test_split_swap_strategy_encoder_simple_route_unwrap`
|
||||
// but manually replacing the executor address
|
||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
||||
// `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f`
|
||||
(bool success,) = tychoRouterAddr.call(
|
||||
hex"4860f9ed0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067c9185300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067a1925b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041fd1c3dfce5afcb47988cc68165d5de64186cedbeb7eee6fc9cd087bceeaacdfe1ab799d60e0c628f24edfd9819b94ed60846dd23240c481f1d6e5470a7815a891c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950100000000"
|
||||
);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 balancerAfter = ALICE.balance;
|
||||
|
||||
assertTrue(success, "Call Failed");
|
||||
assertGt(balancerAfter - balancerBefore, 26173932);
|
||||
}
|
||||
|
||||
function testSplitSwapIntegration() public {
|
||||
// Test created with calldata from our router encoder, replacing the executor
|
||||
// address with the USV2 executor address.
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
approvals::permit2::Permit2,
|
||||
constants::WETH_ADDRESS,
|
||||
swap_encoder::SWAP_ENCODER_REGISTRY,
|
||||
utils::{biguint_to_u256, bytes_to_address, encode_input, percentage_to_uint24},
|
||||
},
|
||||
@@ -109,10 +110,27 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
// runs)
|
||||
intermediary_tokens.sort();
|
||||
|
||||
let (mut unwrap, mut wrap) = (false, false);
|
||||
if let Some(action) = solution.native_action.clone() {
|
||||
match action {
|
||||
NativeAction::Wrap => wrap = true,
|
||||
NativeAction::Unwrap => unwrap = true,
|
||||
}
|
||||
}
|
||||
|
||||
let mut tokens = Vec::with_capacity(2 + intermediary_tokens.len());
|
||||
if wrap {
|
||||
tokens.push(WETH_ADDRESS.clone());
|
||||
} else {
|
||||
tokens.push(solution.given_token.clone());
|
||||
}
|
||||
tokens.extend(intermediary_tokens);
|
||||
|
||||
if unwrap {
|
||||
tokens.push(WETH_ADDRESS.clone());
|
||||
} else {
|
||||
tokens.push(solution.checked_token.clone());
|
||||
}
|
||||
|
||||
let mut swaps = vec![];
|
||||
for swap in solution.swaps.iter() {
|
||||
@@ -165,13 +183,6 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
}
|
||||
|
||||
let encoded_swaps = self.ple_encode(swaps);
|
||||
let (mut unwrap, mut wrap) = (false, false);
|
||||
if let Some(action) = solution.native_action {
|
||||
match action {
|
||||
NativeAction::Wrap => wrap = true,
|
||||
NativeAction::Unwrap => unwrap = true,
|
||||
}
|
||||
}
|
||||
let method_calldata = (
|
||||
biguint_to_u256(&solution.given_amount),
|
||||
bytes_to_address(&solution.given_token)?,
|
||||
@@ -249,7 +260,10 @@ mod tests {
|
||||
use tycho_core::{dto::ProtocolComponent, Bytes};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
use crate::encoding::{
|
||||
evm::constants::{NATIVE_ADDRESS, WETH_ADDRESS},
|
||||
models::Swap,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_executor_strategy_encode() {
|
||||
@@ -437,6 +451,100 @@ mod tests {
|
||||
assert_eq!(hex_calldata[1288..], expected_swaps);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_swap_strategy_encoder_simple_route_wrap() {
|
||||
// Performs a single swap from WETH to DAI on a USV2 pool, wrapping ETH
|
||||
// Note: This test does not assert anything. It is only used to obtain integration test
|
||||
// data for our router solidity test.
|
||||
|
||||
// Set up a mock private key for signing
|
||||
let private_key =
|
||||
"0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string();
|
||||
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: WETH_ADDRESS.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
};
|
||||
|
||||
let encoder = SplitSwapStrategyEncoder::new(private_key, Chain::Ethereum).unwrap();
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
given_token: NATIVE_ADDRESS.clone(),
|
||||
given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
|
||||
checked_token: dai,
|
||||
expected_amount: Some(BigUint::from_str("3_000_000000000000000000").unwrap()),
|
||||
check_amount: None,
|
||||
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||
swaps: vec![swap],
|
||||
native_action: Some(NativeAction::Wrap),
|
||||
..Default::default()
|
||||
};
|
||||
let router_address = Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap();
|
||||
|
||||
let (calldata, _) = encoder
|
||||
.encode_strategy(solution, router_address)
|
||||
.unwrap();
|
||||
|
||||
let hex_calldata = encode(&calldata);
|
||||
println!("{}", hex_calldata);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_swap_strategy_encoder_simple_route_unwrap() {
|
||||
// Performs a single swap from DAI to WETH on a USV2 pool, unwrapping ETH at the end
|
||||
// Note: This test does not assert anything. It is only used to obtain integration test
|
||||
// data for our router solidity test.
|
||||
|
||||
// Set up a mock private key for signing
|
||||
let private_key =
|
||||
"0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string();
|
||||
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai.clone(),
|
||||
token_out: WETH_ADDRESS.clone(),
|
||||
split: 0f64,
|
||||
};
|
||||
|
||||
let encoder = SplitSwapStrategyEncoder::new(private_key, Chain::Ethereum).unwrap();
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
given_token: dai,
|
||||
given_amount: BigUint::from_str("3_000_000000000000000000").unwrap(),
|
||||
checked_token: NATIVE_ADDRESS.clone(),
|
||||
expected_amount: Some(BigUint::from_str("1_000000000000000000").unwrap()),
|
||||
check_amount: None,
|
||||
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||
swaps: vec![swap],
|
||||
native_action: Some(NativeAction::Unwrap),
|
||||
..Default::default()
|
||||
};
|
||||
let router_address = Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap();
|
||||
|
||||
let (calldata, _) = encoder
|
||||
.encode_strategy(solution, router_address)
|
||||
.unwrap();
|
||||
|
||||
let hex_calldata = encode(&calldata);
|
||||
println!("{}", hex_calldata);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_swap_strategy_encoder_complex_route() {
|
||||
// Note: This test does not assert anything. It is only used to obtain integration test
|
||||
|
||||
Reference in New Issue
Block a user