// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; import "@src/CallbackVerificationDispatcher.sol"; import "./TychoRouterTestSetup.sol"; contract CallbackVerificationDispatcherExposed is CallbackVerificationDispatcher { function exposedCallVerifier(bytes calldata data) external returns ( uint256 amountOwed, uint256 amountReceived, address tokenOwed, uint16 dataOffset ) { return _callVerifyCallback(data); } function exposedDecodeVerifierAndSelector(bytes calldata data) external pure returns (address executor, bytes4 selector, bytes memory protocolData) { return _decodeVerifierAndSelector(data); } function exposedSetVerifier(address target) external { callbackVerifiers[target] = true; } function exposedRemoveVerifier(address target) external {} } contract CallbackVerificationDispatcherTest is Constants { CallbackVerificationDispatcherExposed dispatcherExposed; event VerifierSet(address indexed executor); event VerifierRemoved(address indexed executor); function setUp() public { uint256 forkBlock = 20673900; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); dispatcherExposed = new CallbackVerificationDispatcherExposed(); deal(WETH_ADDR, address(dispatcherExposed), 15 ether); deployDummyContract(); } function testCallVerifierSuccess() public { // For this test, we can use any callback verifier and any calldata that we // know works for this verifier. We don't care about which calldata/executor, // since we are only testing the functionality of the staticcall and not // the inner verifier. // Thus, this test case designed from scratch using previously-deployed // Maverick callback verifier. Looking at the code, we can easily design // passing calldata. dispatcherExposed.exposedSetVerifier( address(0x2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8) ); bytes memory data = hex"2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e876b20f8a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; vm.startPrank(address(0xD0b2F5018B5D22759724af6d4281AC0B13266360)); ( uint256 amountOwed, uint256 amountReceived, address tokenOwed, uint16 dataOffset ) = dispatcherExposed.exposedCallVerifier(data); vm.stopPrank(); // The values themselves are irrelevant, we just need to make sure that we // correctly parse the expected output of the existing Maverick verifier assert(amountOwed == 1); assert(amountReceived == 1); assert(tokenOwed == address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)); assert(dataOffset == 148); } function testCallVerifierNoSelector() public { // For this test, we can use any callback verifier and any calldata that we // know works for this verifier. We don't care about which calldata/executor, // since we are only testing the functionality of the staticcall and not // the inner verifier. // Thus, this test case designed from scratch using previously-deployed // Maverick callback verifier. Looking at the code, we can easily design // passing calldata. dispatcherExposed.exposedSetVerifier( address(0x2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8) ); // Pass all-zero selector. This should default to the verifyCallback selector bytes memory data = hex"2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; vm.startPrank(address(0xD0b2F5018B5D22759724af6d4281AC0B13266360)); ( uint256 amountOwed, uint256 amountReceived, address tokenOwed, uint16 dataOffset ) = dispatcherExposed.exposedCallVerifier(data); vm.stopPrank(); // The values themselves are irrelevant, we just need to make sure that we // correctly parse the expected output of the existing Maverick verifier assert(amountOwed == 1); assert(amountReceived == 1); assert(tokenOwed == address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)); assert(dataOffset == 148); } function testCallVerifierBadSelector() public { // A bad selector is provided to an approved executor - causing the call // itself to fail. Make sure this actually reverts. dispatcherExposed.exposedSetVerifier( address(0x2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8) ); vm.startPrank(address(0xD0b2F5018B5D22759724af6d4281AC0B13266360)); bytes memory data = hex"2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8aa0000000000"; vm.expectRevert(bytes("Callback verification failed")); dispatcherExposed.exposedCallVerifier(data); vm.stopPrank(); } function testCallVerifierParseRevertMessage() public { // Verification should fail because caller is not a Maverick pool // Check that we correctly parse the revert message dispatcherExposed.exposedSetVerifier( address(0x2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8) ); bytes memory data = hex"2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; vm.expectRevert( abi.encodeWithSignature( "Error(string)", "Must call from a Maverick Factory Pool" ) ); dispatcherExposed.exposedCallVerifier(data); } function testCallVerifierUnapprovedVerifier() public { bytes memory data = hex"5d622C9053b8FFB1B3465495C8a42E603632bA70aabbccdd1111111111111111"; vm.expectRevert(); dispatcherExposed.exposedCallVerifier(data); } function testDecodeVerifierAndSelector() public { bytes memory data = hex"2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e876b20f8aA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; (address executor, bytes4 selector, bytes memory verifierData) = dispatcherExposed.exposedDecodeVerifierAndSelector(data); assert(executor == address(0x2C960bD1CFE09A26105ad3C351bEa0a3fAD0F8e8)); assert(selector == bytes4(0x76b20f8a)); // Direct bytes comparison not supported - must use keccak assert( keccak256(verifierData) == keccak256(hex"A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") ); } }