-
Notifications
You must be signed in to change notification settings - Fork 110
Test UniswapV3 and UniswapV4 swaps. #1306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,14 +39,73 @@ import {IOAppCore} from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOApp | |||||||||
| import {MessagingFee} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol"; | ||||||||||
| import {OFTAdapter} from "lib/LayerZero-v2/packages/layerzero-v2/evm/oapp/contracts/oft/OFTAdapter.sol"; | ||||||||||
| import {OptionsBuilder} from "lib/LayerZero-v2/packages/layerzero-v2/evm/oapp/contracts/oapp/libs/OptionsBuilder.sol"; | ||||||||||
|
|
||||||||||
| // Safe contracts | ||||||||||
| import {CompatibilityFallbackHandler} from "@safe-smart-account/contracts/handler/CompatibilityFallbackHandler.sol"; | ||||||||||
|
|
||||||||||
| interface IRetrieveDelegates { | ||||||||||
| function delegates(address account) external view returns (address); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| struct PoolKey { | ||||||||||
| /// @notice The lower currency of the pool, sorted numerically. | ||||||||||
| /// For native ETH, Currency currency0 = Currency.wrap(address(0)); | ||||||||||
| address currency0; | ||||||||||
| /// @notice The higher currency of the pool, sorted numerically | ||||||||||
| address currency1; | ||||||||||
| /// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1, the pool has a dynamic fee and must be exactly equal to 0x800000 | ||||||||||
| uint24 fee; | ||||||||||
| /// @notice Ticks that involve positions must be a multiple of tick spacing | ||||||||||
| int24 tickSpacing; | ||||||||||
| /// @notice The hooks of the pool | ||||||||||
| address hooks; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| struct QuoteExactInputSingleParams { | ||||||||||
| address tokenIn; | ||||||||||
| address tokenOut; | ||||||||||
| uint256 amountIn; | ||||||||||
| uint24 fee; | ||||||||||
| uint160 sqrtPriceLimitX96; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| interface IUniswapV3Quoter { | ||||||||||
| function quoteExactInputSingle( | ||||||||||
| address tokenIn, | ||||||||||
| address tokenOut, | ||||||||||
| uint24 fee, | ||||||||||
| uint256 amountIn, | ||||||||||
| uint160 sqrtPriceLimitX96 | ||||||||||
| ) external returns (uint256 amountOut); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| interface ISwapRouter { | ||||||||||
| struct ExactInputSingleParams { | ||||||||||
| address tokenIn; | ||||||||||
| address tokenOut; | ||||||||||
| uint24 fee; | ||||||||||
| address recipient; | ||||||||||
| uint256 amountIn; | ||||||||||
| uint256 amountOutMinimum; | ||||||||||
| uint160 sqrtPriceLimitX96; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| interface IV4Router { | ||||||||||
| struct ExactInputSingleParams { | ||||||||||
| PoolKey poolKey; | ||||||||||
| bool zeroForOne; | ||||||||||
| uint128 amountIn; | ||||||||||
| uint128 amountOutMinimum; | ||||||||||
| bytes hookData; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function execute(bytes memory commands, bytes[] memory inputs, uint256 deadline) | ||||||||||
| external | ||||||||||
| payable; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| contract MOVETokenV2Test is Test { | ||||||||||
| // ============================================================================= | ||||||||||
| // STATE VARIABLES - CONTRACT INSTANCES | ||||||||||
|
|
@@ -125,7 +184,7 @@ contract MOVETokenV2Test is Test { | |||||||||
| uint32 public constant ULN_CONFIG_TYPE = 2; | ||||||||||
| /// @dev LayerZero config type for receive library configuration | ||||||||||
| uint32 public constant RECEIVE_CONFIG_TYPE = 2; | ||||||||||
|
|
||||||||||
| /// @dev Total supply of MOVE tokens (10 billion with 8 decimals) | ||||||||||
| uint256 public constant TOTAL_SUPPLY = 10000000000 * 10 ** 8; | ||||||||||
| /// @dev MOVE token decimals | ||||||||||
|
|
@@ -171,12 +230,16 @@ contract MOVETokenV2Test is Test { | |||||||||
| assertEq(move.hasRole(DEFAULT_ADMIN_ROLE, other), false); | ||||||||||
|
|
||||||||||
| vm.expectRevert( | ||||||||||
| abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, address(this), DEFAULT_ADMIN_ROLE) | ||||||||||
| abi.encodeWithSelector( | ||||||||||
| IAccessControl.AccessControlUnauthorizedAccount.selector, address(this), DEFAULT_ADMIN_ROLE | ||||||||||
| ) | ||||||||||
| ); | ||||||||||
| move.grantRole(DEFAULT_ADMIN_ROLE, other); | ||||||||||
|
|
||||||||||
| vm.prank(labs); | ||||||||||
| vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, labs, DEFAULT_ADMIN_ROLE)); | ||||||||||
| vm.expectRevert( | ||||||||||
| abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, labs, DEFAULT_ADMIN_ROLE) | ||||||||||
| ); | ||||||||||
| move.grantRole(DEFAULT_ADMIN_ROLE, other); | ||||||||||
|
|
||||||||||
| vm.prank(oldFoundation); | ||||||||||
|
|
@@ -246,7 +309,7 @@ contract MOVETokenV2Test is Test { | |||||||||
| address(moveTokenImplementation2), | ||||||||||
| initializeData | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| // Once transaction is scheduled comment out testScheduleAndSetPeer and RERUN TEST to verify that all arguments are correct | ||||||||||
| testScheduleAndSetPeer(); | ||||||||||
|
|
||||||||||
|
|
@@ -354,18 +417,24 @@ contract MOVETokenV2Test is Test { | |||||||||
| assertEq(move2.hasRole(DEFAULT_ADMIN_ROLE, other), false); | ||||||||||
|
|
||||||||||
| vm.prank(other); | ||||||||||
| vm.expectRevert(abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, other, DEFAULT_ADMIN_ROLE)); | ||||||||||
| vm.expectRevert( | ||||||||||
| abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, other, DEFAULT_ADMIN_ROLE) | ||||||||||
| ); | ||||||||||
| move2.grantRole(DEFAULT_ADMIN_ROLE, other); | ||||||||||
|
|
||||||||||
| vm.prank(foundation); | ||||||||||
| vm.expectRevert( | ||||||||||
| abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, foundation, DEFAULT_ADMIN_ROLE) | ||||||||||
| abi.encodeWithSelector( | ||||||||||
| IAccessControl.AccessControlUnauthorizedAccount.selector, foundation, DEFAULT_ADMIN_ROLE | ||||||||||
| ) | ||||||||||
| ); | ||||||||||
| move2.grantRole(DEFAULT_ADMIN_ROLE, other); | ||||||||||
|
|
||||||||||
| vm.prank(oldFoundation); | ||||||||||
| vm.expectRevert( | ||||||||||
| abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, oldFoundation, DEFAULT_ADMIN_ROLE) | ||||||||||
| abi.encodeWithSelector( | ||||||||||
| IAccessControl.AccessControlUnauthorizedAccount.selector, oldFoundation, DEFAULT_ADMIN_ROLE | ||||||||||
| ) | ||||||||||
| ); | ||||||||||
| move2.grantRole(DEFAULT_ADMIN_ROLE, other); | ||||||||||
|
|
||||||||||
|
|
@@ -409,7 +478,7 @@ contract MOVETokenV2Test is Test { | |||||||||
| composeMsg: bytes(""), | ||||||||||
| oftCmd: bytes("") | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| IOFT deprecatedBridge = IOFT(bridge); | ||||||||||
| vm.expectRevert(); // Should fail - bridge is deprecated | ||||||||||
| deprecatedBridge.quoteSend(sendParam, false); | ||||||||||
|
|
@@ -562,10 +631,131 @@ contract MOVETokenV2Test is Test { | |||||||||
| assertEq(move2.totalSupply(), totalSupplyBefore - (amount * 2)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function testUniswap() public { | ||||||||||
| testConfigOFT(); | ||||||||||
| address uniswapv3Router = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; | ||||||||||
| address uniswapv4Router = 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af; | ||||||||||
| address uniswapv3Quoter = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6; | ||||||||||
| address uniswapv4Quoter = 0x61fFE014bA17989E743c5F6cB21bF9697530B21e; | ||||||||||
|
|
||||||||||
| uint256 amount = 100; | ||||||||||
|
|
||||||||||
| // quote from uniswap v3 | ||||||||||
| (bool successV3, bytes memory dataV3) = uniswapv3Quoter.call( | ||||||||||
| abi.encodeWithSignature( | ||||||||||
| "quoteExactInputSingle(address,address,uint24,uint256,uint160)", | ||||||||||
| address(move2), | ||||||||||
| 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH | ||||||||||
| 10000, | ||||||||||
| amount, | ||||||||||
| 0 | ||||||||||
| ) | ||||||||||
| ); | ||||||||||
| require(successV3, "V3 quote failed"); | ||||||||||
| uint256 amountOutV3 = abi.decode(dataV3, (uint256)); | ||||||||||
|
|
||||||||||
| // Adds eth and move balance to uniswapv4 pool | ||||||||||
| vm.deal(0x1B42bb0771690a3A82beDb1BC74933788145CbfD, 100 ether); | ||||||||||
| deal(address(move2), 0x1B42bb0771690a3A82beDb1BC74933788145CbfD, 100 ether); | ||||||||||
| uint24 v4fee = 10000; | ||||||||||
| // quote from uniswap v4 | ||||||||||
| (bool successV4, bytes memory dataV4) = uniswapv4Quoter.call( | ||||||||||
| abi.encodeWithSignature( | ||||||||||
| "quoteExactInputSingle((address,address,uint256,uint24,uint160))", | ||||||||||
| QuoteExactInputSingleParams({ | ||||||||||
| tokenIn: address(move2), | ||||||||||
| tokenOut: address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2), // ETH | ||||||||||
| amountIn: amount, | ||||||||||
| fee: v4fee, | ||||||||||
| sqrtPriceLimitX96: 0 | ||||||||||
| }) | ||||||||||
| ) | ||||||||||
| ); | ||||||||||
| require(successV4, "V4 quote failed"); | ||||||||||
| uint256 amountOutV4 = abi.decode(dataV4, (uint256)); | ||||||||||
|
|
||||||||||
| uint256 balanceBefore = move2.balanceOf(anchorage); | ||||||||||
| uint256 snapshotId = vm.snapshot(); | ||||||||||
|
|
||||||||||
|
Comment on lines
+678
to
+679
|
||||||||||
| uint256 snapshotId = vm.snapshot(); |
Copilot
AI
Oct 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approval for uniswapv4Router serves no purpose since the V4 swap code is commented out. Remove this unused approval or implement the V4 swap functionality.
| vm.prank(anchorage); | |
| move2.approve(uniswapv4Router, amount); | |
| // vm.prank(anchorage); | |
| // move2.approve(uniswapv4Router, amount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardcoded value '100' is a magic number. Consider defining it as a named constant or making it configurable to improve test maintainability.