diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index cb81d58..c2ca63d 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -77,6 +77,7 @@ contract Constants is Test, BaseConstants { // Uniswap v3 address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; + address DAI_USDT_USV3 = 0x48DA0965ab2d2cbf1C17C09cFB5Cbe67Ad5B1406; address USDC_WETH_USV3 = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640; // 0.05% fee address USDC_WETH_USV3_2 = 0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8; // 0.3% fee diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index 3aecf03..730de23 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -33,3 +33,4 @@ test_encode_balancer_v3:7bc3485026ac48b6cf9baf0a377477fff5703af8c71ea051a5f82c67 test_single_encoding_strategy_balancer_v3:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000097ffedb80d4b2ca6105a07a4d90eb739c45a66600000000000000000000000030881baa943777f92dc934d53d3bfdf33382cab300000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000006503a6a84cd762d9707a21605b548aaab891562aab097ffedb80d4b2ca6105a07a4d90eb739c45a66630881baa943777f92dc934d53d3bfdf33382cab3f028ac624074d6793c36dc8a06ecec0f5a39a71800cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000 test_uniswap_v3_balancer_v3:e21dd0d3000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000004a220e6096b25eadb88358cb44068a324825467500000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d200692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed0000006503a6a84cd762d9707a21605b548aaab891562aab2260fac5e5542a773aa44fbcfedf7c193bc2c5994a220e6096b25eadb88358cb44068a3248254675571bea0e99e139cd0b6b7d9352ca872dfe0d72dd01cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000 test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f27700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7f00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000411d60a13b5e993ef8088cb2cb7e16281f47fbc329bd0ec2a48165e7898542a7ed12fe7ffdec713b6d94e99ddf0a384674617b6190b8534491d525ecb090c8e4881c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_sequential_swap_usx:0101e21dd0d300000000000000000000000000000000000000000000006c6b935b8bbd4000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000769cfd80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d9da78b6a5bedca287aa5d49613ba36b90c15c40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470b6b175474e89094c44da98b954eedeac495271d0fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000643ede3eca2a72b3aecc820e955b36f38437d013955777d92f208679db4b9778590fa3cab3ac9e2168010000692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48dac17f958d2ee523a2206206994597c13d831ec70000646d9da78b6a5bedca287aa5d49613ba36b90c15c43416cf6c708da44db2624d63ea0aaef7113527c6010100000000000000000000 diff --git a/foundry/test/uniswap_x/UniswapXFiller.t.sol b/foundry/test/uniswap_x/UniswapXFiller.t.sol index 5128339..be92ca3 100644 --- a/foundry/test/uniswap_x/UniswapXFiller.t.sol +++ b/foundry/test/uniswap_x/UniswapXFiller.t.sol @@ -6,7 +6,7 @@ import "@src/uniswap_x/UniswapXFiller.sol"; import "../TychoRouterTestSetup.sol"; contract UniswapXFillerTest is Test, TychoRouterTestSetup { - address EXECUTOR = makeAddr("executor"); + address EXECUTOR = address(0xCe79b081c0c924cb67848723ed3057234d10FC6b); address REACTOR = address(0x00000011F84B9aa48e5f8aA8B9897600006289Be); UniswapXFiller filler; @@ -18,7 +18,7 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { ); function getForkBlock() public pure override returns (uint256) { - return 22788691; + return 22880493; } function fillerSetup() public { @@ -72,9 +72,7 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { abi.encodePacked(true, true, tychoRouterData); deal(WETH_ADDR, address(filler), amountIn); - vm.startPrank(address(filler)); - IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn); - vm.stopPrank(); + ResolvedOrder[] memory orders = new ResolvedOrder[](1); OutputToken[] memory outputs = new OutputToken[](1); outputs[0] = OutputToken({ @@ -82,7 +80,7 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { amount: 1847751195973566072891, recipient: BOB }); - // Mostly irrelevant fields for this test - we only need token input and outputs + // Irrelevant fields for this test - we only need token output // info for the sake of testing. orders[0] = ResolvedOrder({ info: OrderInfo({ @@ -96,7 +94,6 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { input: InputToken({ token: address(WETH_ADDR), amount: amountIn, - // We need the proper maxAmount for our approval to work maxAmount: amountIn }), outputs: outputs, @@ -121,31 +118,75 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { function testExecuteIntegration() public { fillerSetup(); - // tx: 0x5b602b7d0a37e241bd032a907b9ddf314e9f2fc2104fd91cb55bdb3d8dfe4e9c - // 0.2 WBTC -> USDC + + // Set to time with no more penalty for not being exclusive filler + vm.warp(1752050415); + + deal( + DAI_ADDR, + address(0xD213e6F6dCB2DBaC03FA28b893F6dA1BD822e852), + 2000 ether + ); + + uint256 amountIn = 2000000000000000000000; + + vm.startPrank(address(0xD213e6F6dCB2DBaC03FA28b893F6dA1BD822e852)); + // Approve Permit2 + IERC20(DAI_ADDR).approve( + address(0x000000000022D473030F116dDEE9F6B43aC78BA3), amountIn + ); + vm.stopPrank(); + + // Tx 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d + // Calldata generated using rust test `test_sequential_swap_usx` + SignedOrder memory order = SignedOrder({ - order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000000000000000000000000000000000000001312d000000000000000000000000000000000000000000000000000000000001312d0000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be0000000000000000000000000d1100e55ef6c4e5800f4624b1e6121d798eb696046832163cef9c09382cf582bb878b37a42933ea2bdf33757942ab2747b3500100000000000000000000000000000000000000000000000000000000685d4150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000004f921447c00000000000000000000000000000000000000000000000000000004f1464dea0000000000000000000000000d1100e55ef6c4e5800f4624b1e6121d798eb696000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000330d86400000000000000000000000000000000000000000000000000000000032bce2600000000000000000000000027213e28d7fda5c57fe9e5dd923818dbccf71c4700000000000000000000000000000000000000000000000000000000685d407600000000000000000000000000000000000000000000000000000000685d40b2000000000000000000000000225a38bc71102999dd13478bfabd7c4d53f2dc170000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000004fb7f8815000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417067afde0759ae3653dad5d412519f488b6e9ed8955b3e3b8606e85c0198a9d71075295d33fe84b5ccc9c2d38a7ea79d7fad68128a37cabc5557342756a4e8311b00000000000000000000000000000000000000000000000000000000000000", - sig: hex"41b7a696a04f897d1e4ccaf88136092169c2874242d55c3fe4c028125efe95340f5ce764b9dce9d2cae241d97ceb515d3f1739972ca884ed51b2870045438c3a1c" + order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e8520468320351debb1ddbfb032a239d699e3d54e3ce2b6e1037cd836a784c80b60100000000000000000000000000000000000000000000000000000000686e2bf9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000076f9f4870000000000000000000000000000000000000000000000000000000076566300000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e85200000000000000000000000000000000000000000000000000000000686e2aee00000000000000000000000000000000000000000000000000000000686e2b2a000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000007727b5f40000000000000000000000000000000000000000000000000000000000000041a2d261cd4c8930428260f18b55e3036024bac68d58cb2ee6161e6395b0984b827104158713d44ddc4e14d852b48d93d95a4e60b8d5be1ef431c1e82d2f76a4111b00000000000000000000000000000000000000000000000000000000000000", + sig: hex"f4cc5734820e4ee08519045c83a25b75687756053b3d6c0fda2141380dfa6ef17b40f64d9279f237e96982c6ba53a202e01a4358fd66e027c9bdf200d5626f441c" }); - uint256 amountIn = 0.2 * 10 ** 8; + bytes memory callbackData = + loadCallDataFromFile("test_sequential_swap_usx"); + + vm.startPrank(EXECUTOR); + filler.execute(order, callbackData); + vm.stopPrank(); + } + + function testExecute() public { + fillerSetup(); + + // Set to time with no more penalty for not being exclusive filler + vm.warp(1752050415); + + // tx: 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d + // DAI ──> USDT + SignedOrder memory order = SignedOrder({ + order: hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001000000000000000000000000004449cd34d1eb1fedcf02a1be3834ffde8e6a61800000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000006c6b935b8bbd40000000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000011f84b9aa48e5f8aa8b9897600006289be000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e8520468320351debb1ddbfb032a239d699e3d54e3ce2b6e1037cd836a784c80b60100000000000000000000000000000000000000000000000000000000686e2bf9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000076f9f4870000000000000000000000000000000000000000000000000000000076566300000000000000000000000000d213e6f6dcb2dbac03fa28b893f6da1bd822e85200000000000000000000000000000000000000000000000000000000686e2aee00000000000000000000000000000000000000000000000000000000686e2b2a000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000007727b5f40000000000000000000000000000000000000000000000000000000000000041a2d261cd4c8930428260f18b55e3036024bac68d58cb2ee6161e6395b0984b827104158713d44ddc4e14d852b48d93d95a4e60b8d5be1ef431c1e82d2f76a4111b00000000000000000000000000000000000000000000000000000000000000", + sig: hex"f4cc5734820e4ee08519045c83a25b75687756053b3d6c0fda2141380dfa6ef17b40f64d9279f237e96982c6ba53a202e01a4358fd66e027c9bdf200d5626f441c" + }); + + uint256 amountIn = 2000000000000000000000; bool zeroForOne = true; + uint24 fee = 100; bytes memory protocolData = abi.encodePacked( - WBTC_ADDR, - USDC_WBTC_POOL, + DAI_ADDR, + USDT_ADDR, + fee, fillerAddr, + DAI_USDT_USV3, zeroForOne, RestrictTransferFrom.TransferType.TransferFrom ); bytes memory swap = - encodeSingleSwap(address(usv2Executor), protocolData); + encodeSingleSwap(address(usv3Executor), protocolData); bytes memory tychoRouterData = abi.encodeWithSelector( tychoRouter.singleSwap.selector, amountIn, - WBTC_ADDR, - USDC_ADDR, + DAI_ADDR, + USDT_ADDR, 1, false, false, @@ -164,10 +205,6 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { IERC20(WBTC_ADDR).approve(tychoRouterAddr, amountIn); vm.stopPrank(); - // This is a hack because the tx we are trying to replicate returns a looooot more USDC than what the uni v2 pool does at this point - // 5113180081 is the difference and 54068100 is the fee - deal(USDC_ADDR, address(filler), 5113180081 + 54068100); - vm.startPrank(EXECUTOR); filler.execute(order, callbackData); vm.stopPrank(); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 9d31244..aad4116 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -42,6 +42,10 @@ pub fn pepe() -> Bytes { Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap() } +pub fn usdt() -> Bytes { + Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap() +} + pub fn get_signer() -> PrivateKeySigner { // Set up a mock private key for signing (Alice's pk in our contract tests) let private_key = diff --git a/tests/uniswap_x_integration_tests.rs b/tests/uniswap_x_integration_tests.rs new file mode 100644 index 0000000..8ea134f --- /dev/null +++ b/tests/uniswap_x_integration_tests.rs @@ -0,0 +1,123 @@ +use std::{collections::HashMap, str::FromStr}; + +use alloy::{hex::encode, primitives::Address, sol_types::SolValue}; +use num_bigint::{BigInt, BigUint}; +use tycho_common::{models::protocol::ProtocolComponent, Bytes}; +use tycho_execution::encoding::{ + evm::{ + approvals::protocol_approvals_manager::ProtocolApprovalsManager, + utils::{bytes_to_address, write_calldata_to_file}, + }, + models::{Solution, Swap, UserTransferType}, +}; + +use crate::common::{ + dai, encoding::encode_tycho_router_call, eth, eth_chain, get_tycho_router_encoder, + router_address, usdc, usdt, +}; + +mod common; + +#[test] +fn test_sequential_swap_usx() { + // Replicates real uniswap X order settled in tx: + // 0x005d7b150017ba1b59d2f99395ccae7bda9b739938ade4e509817e32760aaf9d + // Performs a sequential + // swap from DAI to USDT though USDC using USV3 pools + // + // DAI ───(USV3)──> USDC ───(USV2)──> USDT + // Creates all the calldata needed for the uniswap X callbackData + + let filler = Bytes::from_str("0x6D9da78B6A5BEdcA287AA5d49613bA36b90c15C4").unwrap(); + let usx_reactor = Address::from_str("0x00000011F84B9aa48e5f8aA8B9897600006289Be").unwrap(); + + let dai = dai(); + let usdc = usdc(); + let usdt = usdt(); + + let swap_dai_usdc = Swap { + component: ProtocolComponent { + id: "0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168".to_string(), + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs + .insert("fee".to_string(), Bytes::from(BigInt::from(100).to_signed_bytes_be())); + attrs + }, + ..Default::default() + }, + token_in: dai.clone(), + token_out: usdc.clone(), + split: 0f64, + user_data: None, + }; + let swap_usdc_usdt = Swap { + component: ProtocolComponent { + id: "0x3416cF6C708Da44DB2624D63ea0AAef7113527C6".to_string(), + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs + .insert("fee".to_string(), Bytes::from(BigInt::from(100).to_signed_bytes_be())); + attrs + }, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: usdt.clone(), + split: 0f64, + user_data: None, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: dai.clone(), + given_amount: BigUint::from_str("2_000_000000000000000000").unwrap(), + checked_token: usdt.clone(), + checked_amount: BigUint::from_str("1_990_000000").unwrap(), + sender: filler.clone(), + receiver: filler.clone(), + swaps: vec![swap_dai_usdc, swap_usdc_usdt], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let tycho_calldata = encode_tycho_router_call( + eth_chain().id(), + encoded_solution, + &solution, + &UserTransferType::TransferFrom, + ð(), + None, + ) + .unwrap() + .data; + + // Uniswap X specific part + let filler_address = bytes_to_address(&filler).unwrap(); + let token_approvals_manager = ProtocolApprovalsManager::new().unwrap(); + + let token_in_approval_needed = token_approvals_manager + .approval_needed( + bytes_to_address(&dai).unwrap(), + filler_address, + bytes_to_address(&router_address()).unwrap(), + ) + .unwrap(); + + let token_out_approval_needed = token_approvals_manager + .approval_needed(bytes_to_address(&usdc).unwrap(), filler_address, usx_reactor) + .unwrap(); + + let full_calldata = + (token_in_approval_needed, token_out_approval_needed, tycho_calldata).abi_encode_packed(); + + let hex_calldata = encode(&full_calldata); + write_calldata_to_file("test_sequential_swap_usx", hex_calldata.as_str()); +}