Merge pull request #78 from propeller-heads/encoding/tnl/ENG-4259-native-eth-integration

fix: Native ETH input/output integration tests/fixes
This commit is contained in:
Tamara
2025-02-21 09:50:22 -05:00
committed by GitHub
4 changed files with 198 additions and 9 deletions

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -735,8 +735,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
@@ -750,8 +749,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"
@@ -765,6 +763,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 USDC to ETH 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_out`
// 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.

View File

@@ -1034,7 +1034,6 @@ 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);
} }
@@ -1118,4 +1117,129 @@ mod tests {
assert_eq!(hex_calldata, expected_input); assert_eq!(hex_calldata, expected_input);
println!("{}", hex_calldata); println!("{}", hex_calldata);
} }
#[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(eth_chain(), swap_encoder_registry, Some(private_key))
.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(eth_chain(), swap_encoder_registry, Some(private_key))
.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);
}
} }