fix: Native ETH input/output integration tests/fixes
- Only for single swaps - Used USV4 for this because it's the only DEX we support that allows native ETH swaps - For Native ETH input single swaps, we need to properly check the remaining amount (we were treating them wronly like ERC20 tokens) - For Native ETH output single swaps, we were passing the incorrect currency (the settle always needs to be the out token and the take always needs to be the in token, this should not depend on the zeroForOne value).
This commit is contained in:
@@ -124,7 +124,13 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
|
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 leftoverAmountIn = IERC20(tokenIn).balanceOf(address(this));
|
uint256 leftoverAmountIn;
|
||||||
|
if (tokenIn == address(0)) {
|
||||||
|
leftoverAmountIn = address(this).balance;
|
||||||
|
} else {
|
||||||
|
leftoverAmountIn = IERC20(tokenIn).balanceOf(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
if (leftoverAmountIn > 0) {
|
if (leftoverAmountIn > 0) {
|
||||||
revert TychoRouter__AmountInNotFullySpent(leftoverAmountIn);
|
revert TychoRouter__AmountInNotFullySpent(leftoverAmountIn);
|
||||||
}
|
}
|
||||||
@@ -209,7 +215,13 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
|
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 leftoverAmountIn = IERC20(tokenIn).balanceOf(address(this));
|
uint256 leftoverAmountIn;
|
||||||
|
if (tokenIn == address(0)) {
|
||||||
|
leftoverAmountIn = address(this).balance;
|
||||||
|
} else {
|
||||||
|
leftoverAmountIn = IERC20(tokenIn).balanceOf(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
if (leftoverAmountIn > 0) {
|
if (leftoverAmountIn > 0) {
|
||||||
revert TychoRouter__AmountInNotFullySpent(leftoverAmountIn);
|
revert TychoRouter__AmountInNotFullySpent(leftoverAmountIn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ contract UniswapV4Executor is IExecutor, V4Router {
|
|||||||
hookData: bytes("")
|
hookData: bytes("")
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
params[1] = abi.encode(key.currency0, amountIn);
|
params[1] = abi.encode(tokenIn, amountIn); // currency to settle
|
||||||
params[2] = abi.encode(key.currency1, uint256(0));
|
params[2] = abi.encode(tokenOut, uint256(0)); // currency to take
|
||||||
swapData = abi.encode(actions, params);
|
swapData = abi.encode(actions, params);
|
||||||
} else {
|
} else {
|
||||||
PathKey[] memory path = new PathKey[](pools.length);
|
PathKey[] memory path = new PathKey[](pools.length);
|
||||||
|
|||||||
@@ -734,8 +734,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testUSV4Integration() public {
|
function testUSV4Integration() public {
|
||||||
// Test created with calldata from our router encoder, replacing the executor
|
// Test created with calldata from our router encoder.
|
||||||
// address with the USV4 executor address.
|
|
||||||
|
|
||||||
// Performs a sequential swap from USDC to PEPE though ETH using two
|
// Performs a sequential swap from USDC to PEPE though ETH using two
|
||||||
// consecutive USV4 pools
|
// consecutive USV4 pools
|
||||||
@@ -749,8 +748,7 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
vm.startPrank(ALICE);
|
vm.startPrank(ALICE);
|
||||||
IERC20(USDC_ADDR).approve(address(permit2Address), type(uint256).max);
|
IERC20(USDC_ADDR).approve(address(permit2Address), type(uint256).max);
|
||||||
// Encoded solution generated using `test_split_encoding_strategy_usv4`
|
// Encoded solution generated using `test_split_encoding_strategy_usv4`
|
||||||
// but manually replacing the executor address
|
// and ensuring that the encoded executor address is the one in this test
|
||||||
// `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test
|
|
||||||
// `f62849f9a0b5bf2913b396098f7c7019b51a820a`
|
// `f62849f9a0b5bf2913b396098f7c7019b51a820a`
|
||||||
(bool success,) = tychoRouterAddr.call(
|
(bool success,) = tychoRouterAddr.call(
|
||||||
hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000067ddee9e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067b668a6000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041bdf91011918dcb5f59ab3588212a035c770a2839fe2c19060491370fa89685b8469def9e83c7b9cf8f0ef5088a3179556a6ba1096cefbe83c09a1182981c93e41c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009400920001000000f62849f9a0b5bf2913b396098f7c7019b51a820abd0625aba0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a91dd73460000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f4000000000000000000000000"
|
hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000067ddee9e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067b668a6000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041bdf91011918dcb5f59ab3588212a035c770a2839fe2c19060491370fa89685b8469def9e83c7b9cf8f0ef5088a3179556a6ba1096cefbe83c09a1182981c93e41c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009400920001000000f62849f9a0b5bf2913b396098f7c7019b51a820abd0625aba0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a91dd73460000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f4000000000000000000000000"
|
||||||
@@ -764,6 +762,61 @@ contract TychoRouterTest is TychoRouterTestSetup {
|
|||||||
assertEq(balancerAfter - balancerBefore, 97191013220606467325121599);
|
assertEq(balancerAfter - balancerBefore, 97191013220606467325121599);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testUSV4IntegrationInputETH() public {
|
||||||
|
// Test created with calldata from our router encoder.
|
||||||
|
|
||||||
|
// Performs a single swap from ETH to PEPE without wrapping or unwrapping
|
||||||
|
//
|
||||||
|
// ETH ───(USV4)──> PEPE
|
||||||
|
//
|
||||||
|
deal(ALICE, 1 ether);
|
||||||
|
uint256 balancerBefore = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||||
|
|
||||||
|
// Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in`
|
||||||
|
// and ensuring that the encoded executor address is the one in this test
|
||||||
|
// `f62849f9a0b5bf2913b396098f7c7019b51a820a`
|
||||||
|
(bool success,) = tychoRouterAddr.call{value: 1 ether}(
|
||||||
|
hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067def8e900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067b772f100000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004192bf6f59a6e114588b4b5fb00f3acae3eb2dd18b673924b9cf27d1414be469b70113e4ceef228e11c91d178fea26673d9edcd013dee23fa3c45abdfcb573c9371c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a00780001000000f62849f9a0b5bf2913b396098f7c7019b51a820abd0625ab00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a91dd73466982508145454ce325ddbe47a25d4ec3d23119330061a80001f4000000000000"
|
||||||
|
);
|
||||||
|
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
uint256 balancerAfter = IERC20(PEPE_ADDR).balanceOf(ALICE);
|
||||||
|
|
||||||
|
assertTrue(success, "Call Failed");
|
||||||
|
assertEq(balancerAfter - balancerBefore, 242373460199848577067005852);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testUSV4IntegrationOutputETH() public {
|
||||||
|
// Test created with calldata from our router encoder.
|
||||||
|
|
||||||
|
// Performs a single swap from ETH to PEPE without wrapping or unwrapping
|
||||||
|
//
|
||||||
|
// USDC ───(USV4)──> ETH
|
||||||
|
//
|
||||||
|
deal(USDC_ADDR, ALICE, 3000_000000);
|
||||||
|
uint256 balancerBefore = ALICE.balance;
|
||||||
|
|
||||||
|
// Approve permit2
|
||||||
|
vm.startPrank(ALICE);
|
||||||
|
IERC20(USDC_ADDR).approve(address(permit2Address), type(uint256).max);
|
||||||
|
|
||||||
|
// Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in`
|
||||||
|
// and ensuring that the encoded executor address is the one in this test
|
||||||
|
// `f62849f9a0b5bf2913b396098f7c7019b51a820a`
|
||||||
|
(bool success,) = tychoRouterAddr.call(
|
||||||
|
hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000067df206000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067b79a68000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041d603720f916c45405d4655476fc8f5d5e93e561d1bc1bbd944f865ac2b53638e28fa06fde0c1097d688029c85940a53ba54902b42d17378159ae4affb8b958b01b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a00780001000000f62849f9a0b5bf2913b396098f7c7019b51a820abd0625aba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a91dd73460000000000000000000000000000000000000000000bb800003c000000000000"
|
||||||
|
);
|
||||||
|
|
||||||
|
vm.stopPrank();
|
||||||
|
|
||||||
|
uint256 balancerAfter = ALICE.balance;
|
||||||
|
|
||||||
|
assertTrue(success, "Call Failed");
|
||||||
|
console.logUint(balancerAfter - balancerBefore);
|
||||||
|
assertEq(balancerAfter - balancerBefore, 1117254495486192350);
|
||||||
|
}
|
||||||
|
|
||||||
function testSingleSwapWithWrapIntegration() public {
|
function testSingleSwapWithWrapIntegration() public {
|
||||||
// Test created with calldata from our router encoder, replacing the executor
|
// Test created with calldata from our router encoder, replacing the executor
|
||||||
// address with the USV2 executor address.
|
// address with the USV2 executor address.
|
||||||
|
|||||||
@@ -1006,8 +1006,129 @@ mod tests {
|
|||||||
));
|
));
|
||||||
let hex_calldata = encode(&calldata);
|
let hex_calldata = encode(&calldata);
|
||||||
|
|
||||||
println!("{}", hex_calldata);
|
|
||||||
assert_eq!(hex_calldata[..520], expected_input);
|
assert_eq!(hex_calldata[..520], expected_input);
|
||||||
assert_eq!(hex_calldata[1288..], expected_swaps);
|
assert_eq!(hex_calldata[1288..], expected_swaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_split_encoding_strategy_usv4_eth_in() {
|
||||||
|
// Performs a single swap from ETH to PEPE using a USV4 pool
|
||||||
|
// Note: This test does not assert anything. It is only used to obtain integration test
|
||||||
|
// data for our router solidity test.
|
||||||
|
//
|
||||||
|
// ETH ───(USV4)──> PEPE
|
||||||
|
//
|
||||||
|
// Set up a mock private key for signing (Alice's pk in our router tests)
|
||||||
|
let private_key =
|
||||||
|
"0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string();
|
||||||
|
|
||||||
|
let eth = eth();
|
||||||
|
let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap();
|
||||||
|
|
||||||
|
let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be());
|
||||||
|
let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be());
|
||||||
|
let mut static_attributes_eth_pepe: HashMap<String, Bytes> = HashMap::new();
|
||||||
|
static_attributes_eth_pepe.insert("fee".into(), pool_fee_eth_pepe);
|
||||||
|
static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe);
|
||||||
|
|
||||||
|
let swap_eth_pepe = Swap {
|
||||||
|
component: ProtocolComponent {
|
||||||
|
id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9"
|
||||||
|
.to_string(),
|
||||||
|
protocol_system: "uniswap_v4".to_string(),
|
||||||
|
static_attributes: static_attributes_eth_pepe,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
token_in: eth.clone(),
|
||||||
|
token_out: pepe.clone(),
|
||||||
|
split: 0f64,
|
||||||
|
};
|
||||||
|
let swap_encoder_registry = get_swap_encoder_registry();
|
||||||
|
let encoder =
|
||||||
|
SplitSwapStrategyEncoder::new(private_key, eth_chain(), swap_encoder_registry).unwrap();
|
||||||
|
|
||||||
|
let solution = Solution {
|
||||||
|
exact_out: false,
|
||||||
|
given_token: eth,
|
||||||
|
given_amount: BigUint::from_str("1_000000000000000000").unwrap(),
|
||||||
|
checked_token: pepe,
|
||||||
|
expected_amount: Some(BigUint::from_str("300_000_000000000000000000").unwrap()),
|
||||||
|
checked_amount: None,
|
||||||
|
slippage: None,
|
||||||
|
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||||
|
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||||
|
router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
|
||||||
|
swaps: vec![swap_eth_pepe],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (calldata, _, _) = encoder
|
||||||
|
.encode_strategy(solution)
|
||||||
|
.unwrap();
|
||||||
|
let hex_calldata = encode(&calldata);
|
||||||
|
|
||||||
|
println!("{}", hex_calldata);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_split_encoding_strategy_usv4_eth_out() {
|
||||||
|
// Performs a single swap from USDC to ETH using a USV4 pool
|
||||||
|
// Note: This test does not assert anything. It is only used to obtain integration test
|
||||||
|
// data for our router solidity test.
|
||||||
|
//
|
||||||
|
// USDC ───(USV4)──> ETH
|
||||||
|
//
|
||||||
|
// Set up a mock private key for signing (Alice's pk in our router tests)
|
||||||
|
let private_key =
|
||||||
|
"0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string();
|
||||||
|
|
||||||
|
let eth = eth();
|
||||||
|
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||||
|
|
||||||
|
// Fee and tick spacing information for this test is obtained by querying the
|
||||||
|
// USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e
|
||||||
|
// Using the poolKeys function with the first 25 bytes of the pool id
|
||||||
|
let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be());
|
||||||
|
let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be());
|
||||||
|
let mut static_attributes_usdc_eth: HashMap<String, Bytes> = HashMap::new();
|
||||||
|
static_attributes_usdc_eth.insert("fee".into(), pool_fee_usdc_eth);
|
||||||
|
static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth);
|
||||||
|
|
||||||
|
let swap_usdc_eth = Swap {
|
||||||
|
component: ProtocolComponent {
|
||||||
|
id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d"
|
||||||
|
.to_string(),
|
||||||
|
protocol_system: "uniswap_v4".to_string(),
|
||||||
|
static_attributes: static_attributes_usdc_eth,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
token_in: usdc.clone(),
|
||||||
|
token_out: eth.clone(),
|
||||||
|
split: 0f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
let swap_encoder_registry = get_swap_encoder_registry();
|
||||||
|
let encoder =
|
||||||
|
SplitSwapStrategyEncoder::new(private_key, eth_chain(), swap_encoder_registry).unwrap();
|
||||||
|
let solution = Solution {
|
||||||
|
exact_out: false,
|
||||||
|
given_token: usdc,
|
||||||
|
given_amount: BigUint::from_str("3000_000000").unwrap(),
|
||||||
|
checked_token: eth,
|
||||||
|
expected_amount: Some(BigUint::from_str("1_000000000000000000").unwrap()),
|
||||||
|
checked_amount: None,
|
||||||
|
slippage: None,
|
||||||
|
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||||
|
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
|
||||||
|
router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
|
||||||
|
swaps: vec![swap_usdc_eth],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (calldata, _, _) = encoder
|
||||||
|
.encode_strategy(solution)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let hex_calldata = encode(&calldata);
|
||||||
|
println!("{}", hex_calldata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user