Merge pull request #237 from propeller-heads/usx/dc/ENG-4673-integration-test

test: Add encoding test for a uniswap X integration test
This commit is contained in:
Tamara
2025-07-14 11:37:51 -04:00
committed by GitHub
5 changed files with 187 additions and 21 deletions

View File

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

View File

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

View File

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

View File

@@ -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 =

View File

@@ -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,
&eth(),
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());
}