diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..4072fb8 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(forge test:*)", + "WebFetch(domain:raw.githubusercontent.com)" + ], + "deny": [], + "ask": [] + } +} \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 8b63a2e..c1420ee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,16 @@ [submodule "packages/foundry/lib/v4-periphery"] path = packages/foundry/lib/v4-periphery url = https://github.com/Uniswap/v4-periphery +[submodule "packages/foundry/lib/v3-core"] + path = packages/foundry/lib/v3-core + url = https://github.com/uniswap/v3-core +[submodule "packages/foundry/lib/v2-core"] + path = packages/foundry/lib/v2-core + url = https://github.com/uniswap/v2-core +[submodule "lib/universal-router"] + branch = main +[submodule "lib/universal-router.git"] + branch = main +[submodule "packages/foundry/lib/universal-router"] + path = packages/foundry/lib/universal-router + url = https://github.com/Uniswap/universal-router diff --git a/packages/foundry/contracts/MockOptionToken.sol b/packages/foundry/contracts/MockOptionToken.sol index 414d3b6..c29928f 100644 --- a/packages/foundry/contracts/MockOptionToken.sol +++ b/packages/foundry/contracts/MockOptionToken.sol @@ -4,12 +4,12 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import "../contracts/OpHook.sol"; import "../contracts/IOptionToken.sol"; -import {HookMiner} from "lib/uniswap-hooks/lib/v4-periphery/src/utils/HookMiner.sol"; -import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; -import {PoolManager} from "v4-core/src/PoolManager.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; -import {PoolKey} from "v4-core/src/types/PoolKey.sol"; -import {Currency} from "v4-core/src/types/Currency.sol"; +import {HookMiner} from "@uniswap/v4-periphery/src/utils/HookMiner.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/packages/foundry/contracts/OpHook.sol b/packages/foundry/contracts/OpHook.sol index de6e5a4..16b3bb3 100644 --- a/packages/foundry/contracts/OpHook.sol +++ b/packages/foundry/contracts/OpHook.sol @@ -3,12 +3,13 @@ pragma solidity ^0.8.26; import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; -import {IPoolManager, SwapParams} from "v4-core/src/interfaces/IPoolManager.sol"; -import {PoolKey} from "v4-core/src/types/PoolKey.sol"; -import {PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; -import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/src/types/BeforeSwapDelta.sol"; -import {Currency} from "v4-core/src/types/Currency.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; +import {IPoolManager, SwapParams} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {BeforeSwapDelta, toBeforeSwapDelta, BeforeSwapDeltaLibrary} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -20,13 +21,15 @@ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + import {OptionPrice, IUniswapV3Pool} from "./OptionPrice.sol"; import {IOptionToken} from "./IOptionToken.sol"; import {IPermit2} from "./IPermit2.sol"; -import {IHooks} from "v4-core/src/interfaces/IHooks.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {console} from "forge-std/console.sol"; +import {NonzeroDeltaCount} from "lib/uniswap-hooks/lib/v4-core/src/libraries/NonzeroDeltaCount.sol"; uint160 constant SQRT_PRICE_X96 = 1<<96; int24 constant TICK_SPACING = type(int16).max; @@ -160,14 +163,14 @@ contract OpHook is BaseHook, ERC4626, Ownable, ReentrancyGuard, Pausable { } require(amountSpecified < 0, "amountSpecified must be negative"); uint256 amount = uint256(-amountSpecified); - int128 amount_ = int128(int256(amount)); + int128 amount_ = SafeCast.toInt128(int256(amount)); uint256 collateralPrice = getCollateralPrice(); uint256 price = _getPrice(collateralPrice, option); uint256 collateralAmount = calculateCollateral(amount, price); - int128 collateralAmount_ = int128(int256(collateralAmount)); + int128 collateralAmount_ = SafeCast.toInt128(int256(collateralAmount)); uint256 cashAmount = Math.mulDiv(amount, 1e36, price); - int128 cashAmount_ = int128(int256(cashAmount)); + int128 cashAmount_ = SafeCast.toInt128(int256(cashAmount)); amounts = Amount({ amount:amount, amount_:amount_, @@ -253,11 +256,27 @@ contract OpHook is BaseHook, ERC4626, Ownable, ReentrancyGuard, Pausable { if (a.cashForOption) { // Here we JIT create option tokens and let the flash accounting handle transfers option.mint(a.collateralAmount); + console.log("delta", NonzeroDeltaCount.read()); poolManager.take(a.cashCurrency, address(this), a.amount); + console.log("delta", NonzeroDeltaCount.read()); + poolManager.sync(a.optionCurrency); option.transfer(address(poolManager_), a.collateralAmount); + console.log("delta", NonzeroDeltaCount.read()); + console.log("option totalSupply", option.balanceOf(sender)); poolManager.settle(); - return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(a.amount_, -a.collateralAmount_), 0); + console.log("option totalSupply", option.balanceOf(sender)); + console.log("delta", NonzeroDeltaCount.read()); + BeforeSwapDelta delta = toBeforeSwapDelta(a.amount_, -a.collateralAmount_); + console.log("a.amount_", a.amount_); + console.log("a.collateralAmount_", a.collateralAmount_); + // console.log("delta", BeforeSwapDelta.unwrap(delta)); + console.log("specifiedDelta", BeforeSwapDeltaLibrary.getSpecifiedDelta(delta)); + console.log("unspecifiedDelta", BeforeSwapDeltaLibrary.getUnspecifiedDelta(delta)); + // int256 dW = poolManager.currencyDelta(msg.sender, Currency.wrap(address(weth))); + // int256 dO2 = poolManager.currencyDelta(msg.sender, Currency.wrap(address(option2))); + // console.log("ownerDeltas", d0, d1 /*, dW, dO2*/); + return (BaseHook.beforeSwap.selector, delta, 0); } else { // Here we have to take the option tokens from the caller and burn them poolManager.take(a.optionCurrency, address(this), a.amount); @@ -268,7 +287,7 @@ contract OpHook is BaseHook, ERC4626, Ownable, ReentrancyGuard, Pausable { } } - function _beforeAddLiquidity(address, PoolKey calldata key, SwapParams calldata params, bytes calldata) + function _beforeAddLiquidity(address, PoolKey calldata, SwapParams calldata, bytes calldata) internal pure @@ -276,7 +295,7 @@ contract OpHook is BaseHook, ERC4626, Ownable, ReentrancyGuard, Pausable { revert("Cannot Add Liquidity to This Pool "); } - function _beforeDonate(address, PoolKey calldata key, SwapParams calldata params, bytes calldata) + function _beforeDonate(address, PoolKey calldata, SwapParams calldata, bytes calldata) internal pure returns (bytes4, BeforeSwapDelta, uint24){ diff --git a/packages/foundry/foundry.lock b/packages/foundry/foundry.lock index 2aa3117..b76473e 100644 --- a/packages/foundry/foundry.lock +++ b/packages/foundry/foundry.lock @@ -1,5 +1,35 @@ { + "lib/v2-core": { + "tag": { + "name": "v1.0.1", + "rev": "4dd59067c76dea4a0e8e4bfdda41877a6b16dedc" + } + }, + "packages/foundry/lib/v4-periphery": { + "rev": "60cd93803ac2b7fa65fd6cd351fd5fd4cc8c9db5" + }, + "lib/universal-router.git": { + "branch": { + "name": "main", + "rev": "3663f6db6e2fe121753cd2d899699c2dc75dca86" + } + }, + "packages/foundry/lib/v3-core": { + "rev": "e3589b192d0be27e100cd0daaf6c97204fdb1899" + }, "lib/v4-periphery": { "rev": "60cd93803ac2b7fa65fd6cd351fd5fd4cc8c9db5" + }, + "lib/v3-core": { + "tag": { + "name": "v1.0.0", + "rev": "e3589b192d0be27e100cd0daaf6c97204fdb1899" + } + }, + "lib/universal-router": { + "branch": { + "name": "main", + "rev": "3663f6db6e2fe121753cd2d899699c2dc75dca86" + } } } \ No newline at end of file diff --git a/packages/foundry/foundry.toml b/packages/foundry/foundry.toml index 1502869..0679003 100644 --- a/packages/foundry/foundry.toml +++ b/packages/foundry/foundry.toml @@ -56,15 +56,6 @@ int_types = "long" # See more config options https://book.getfoundry.sh/reference/config/overview -[profile.default] -optimizer_runs = 44444444 -via_ir = true -ffi = true -fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}, { access = "read", path = "./out"}, {access = "read", path = "./test/bin"}] -solc = "0.8.26" -evm_version = "cancun" -gas_limit = "300000000" -bytecode_hash = "none" [profile.default.fuzz] runs = 1000 diff --git a/packages/foundry/lib/universal-router b/packages/foundry/lib/universal-router new file mode 160000 index 0000000..3663f6d --- /dev/null +++ b/packages/foundry/lib/universal-router @@ -0,0 +1 @@ +Subproject commit 3663f6db6e2fe121753cd2d899699c2dc75dca86 diff --git a/packages/foundry/lib/v2-core b/packages/foundry/lib/v2-core new file mode 160000 index 0000000..4dd5906 --- /dev/null +++ b/packages/foundry/lib/v2-core @@ -0,0 +1 @@ +Subproject commit 4dd59067c76dea4a0e8e4bfdda41877a6b16dedc diff --git a/packages/foundry/lib/v3-core b/packages/foundry/lib/v3-core new file mode 160000 index 0000000..d8b1c63 --- /dev/null +++ b/packages/foundry/lib/v3-core @@ -0,0 +1 @@ +Subproject commit d8b1c635c275d2a9450bd6a78f3fa2484fef73eb diff --git a/packages/foundry/remappings.txt b/packages/foundry/remappings.txt index ee54109..2c02ef1 100644 --- a/packages/foundry/remappings.txt +++ b/packages/foundry/remappings.txt @@ -1,4 +1,8 @@ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts @openzeppelin/uniswap-hooks/=lib/uniswap-hooks/ -v4-core/=lib/uniswap-hooks/lib/v4-core/ -v4-periphery/=lib/uniswap-hooks/lib/v4-periphery/ +@uniswap/v4-periphery/=lib/uniswap-hooks/lib/v4-periphery/ +@uniswap/universal-router/=lib/universal-router/ +@uniswap/v4-core/=lib/uniswap-hooks/lib/v4-core/ +@uniswap/v3-core/=lib/v3-core/ +@uniswap/v2-core/=lib/v2-core/ +v4-core/=lib/uniswap-hooks/lib/v4-core/ \ No newline at end of file diff --git a/packages/foundry/script/DeployLocal.sol b/packages/foundry/script/DeployLocal.sol index 424275a..7c9b79f 100644 --- a/packages/foundry/script/DeployLocal.sol +++ b/packages/foundry/script/DeployLocal.sol @@ -7,8 +7,8 @@ import "../contracts/OpHook.sol"; import "../contracts/IOptionToken.sol"; import "../contracts/MockOptionToken.sol"; import {HookMiner} from "lib/uniswap-hooks/lib/v4-periphery/src/utils/HookMiner.sol"; -import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IWETH9} from "lib/v4-periphery/src/interfaces/external/IWETH9.sol"; @@ -16,12 +16,12 @@ import {IWETH9} from "lib/v4-periphery/src/interfaces/external/IWETH9.sol"; import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; -import {IPoolManager, SwapParams} from "v4-core/src/interfaces/IPoolManager.sol"; -import {PoolKey} from "v4-core/src/types/PoolKey.sol"; -import {PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; -import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/src/types/BeforeSwapDelta.sol"; -import {Currency} from "v4-core/src/types/Currency.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; +import {IPoolManager, SwapParams} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {BeforeSwapDelta, toBeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -37,7 +37,7 @@ import {OptionPrice, IUniswapV3Pool} from "../contracts/OptionPrice.sol"; import {IOptionToken} from "../contracts/IOptionToken.sol"; import {IPermit2} from "../contracts/IPermit2.sol"; -import {IHooks} from "v4-core/src/interfaces/IHooks.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; contract MockERC20 is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) { diff --git a/packages/foundry/test/EndToEnd.t.sol b/packages/foundry/test/EndToEnd.t.sol index d54adc1..968c764 100644 --- a/packages/foundry/test/EndToEnd.t.sol +++ b/packages/foundry/test/EndToEnd.t.sol @@ -8,19 +8,19 @@ import {OptionPrice} from "../contracts/OptionPrice.sol"; import {OpHook} from "../contracts/OpHook.sol"; // Uniswap V4 imports -import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {PoolKey} from "v4-core/src/types/PoolKey.sol"; -import {Currency} from "v4-core/src/types/Currency.sol"; -import {SwapParams} from "v4-core/src/interfaces/IPoolManager.sol"; -import {PoolManager} from "v4-core/src/PoolManager.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {SwapParams} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; import {HookMiner} from "lib/uniswap-hooks/lib/v4-periphery/src/utils/HookMiner.sol"; import {Constants} from "@uniswap/v4-core/test/utils/Constants.sol"; -import {PoolSwapTest} from "v4-core/src/test/PoolSwapTest.sol"; -import {IHooks} from "v4-core/src/interfaces/IHooks.sol"; +import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; -// import {Deployers} from "v4-core/test/utils/Deployers.sol"; +// import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; contract TestERC20 is IERC20 { string public name; diff --git a/packages/foundry/test/OpHook.t.sol b/packages/foundry/test/OpHook.t.sol index a2c0c6f..d76e8bc 100644 --- a/packages/foundry/test/OpHook.t.sol +++ b/packages/foundry/test/OpHook.t.sol @@ -7,23 +7,29 @@ import "../contracts/OpHook.sol"; import "../contracts/IOptionToken.sol"; import "../contracts/MockOptionToken.sol"; import {HookMiner} from "lib/uniswap-hooks/lib/v4-periphery/src/utils/HookMiner.sol"; -import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; + +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import {IWETH9} from "v4-periphery/src/interfaces/external/IWETH9.sol"; +import {IWETH9} from "@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol"; +import { UniversalRouter } from "@uniswap/universal-router/contracts/UniversalRouter.sol"; +import { IV4Router } from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol"; +import { Actions } from "@uniswap/v4-periphery/src/libraries/Actions.sol"; +import { Commands } from "@uniswap/universal-router/contracts/libraries/Commands.sol"; +import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol"; import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; -import {SwapParams, PoolKey} from "v4-core/src/types/PoolOperation.sol"; -import {PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; -import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/src/types/BeforeSwapDelta.sol"; -import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol"; -import {Currency} from "v4-core/src/types/Currency.sol"; -import {TickMath} from "v4-core/src/libraries/TickMath.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; +import {SwapParams, PoolKey} from "@uniswap/v4-core/src/types/PoolOperation.sol"; +import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {BeforeSwapDelta, toBeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; +import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -39,46 +45,61 @@ import {OptionPrice, IUniswapV3Pool} from "../contracts/OptionPrice.sol"; import {IOptionToken} from "../contracts/IOptionToken.sol"; import {IPermit2} from "../contracts/IPermit2.sol"; -import {IHooks} from "v4-core/src/interfaces/IHooks.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {ConstantsMainnet} from "../contracts/ConstantsMainnet.sol"; import {SafeCallback} from "./SafeCallback.sol"; +import {NonzeroDeltaCount} from "lib/uniswap-hooks/lib/v4-core/src/libraries/NonzeroDeltaCount.sol"; + contract SwapCallback is SafeCallback { OpHook public opHook; PoolKey public poolKey; + using CurrencySettler for Currency; + constructor(IPoolManager _poolManager, OpHook _opHook, PoolKey memory _poolKey) SafeCallback(_poolManager) { poolKey = _poolKey; opHook = _opHook; } function _unlockCallback(bytes calldata data) internal override returns (bytes memory) { - // (...) = abi.decode(data, (...)); + (address sender) = abi.decode(data, (address)); + int256 amountIn = 1e6; SwapParams memory params = SwapParams({ zeroForOne: false, - amountSpecified: -1e6, + amountSpecified: -amountIn, sqrtPriceLimitX96: TickMath.MAX_SQRT_PRICE-1 }); bytes memory d = bytes(""); IERC20 usdc = IERC20(Currency.unwrap(poolKey.currency1)); IERC20 option = IERC20(Currency.unwrap(poolKey.currency0)); + uint256 initBal = usdc.balanceOf(address(poolManager)); + poolKey.currency1.settle(poolManager, sender, 1e6, false); + console.log("delta", NonzeroDeltaCount.read()); + BalanceDelta delta = poolManager.swap(poolKey, params, d); - poolManager.sync(poolKey.currency1); - usdc.transfer(address(poolManager), 1e6); - poolManager.take(poolKey.currency0, address(this), uint128(delta.amount0())); - poolManager.settle(); console.log("delta0", delta.amount0()); console.log("delta1", delta.amount1()); + console.log("delta", NonzeroDeltaCount.read()); + // usdc.transfer(address(poolManager), 1e6); + poolKey.currency0.take(poolManager, address(this), uint128(delta.amount0()), false); + // poolManager.take(poolKey.currency0, address(this), uint128(delta.amount0())); + console.log("delta", NonzeroDeltaCount.read()); + // poolManager.sync(poolKey.currency1); + // console.log("delta", NonzeroDeltaCount.read()); + // poolManager.settle(); + console.log("delta", NonzeroDeltaCount.read()); console.log("option balance", option.balanceOf(address(poolManager))); console.log("option balance", option.balanceOf(address(this))); - console.log("usdc balance", usdc.balanceOf(address(poolManager))); - console.log("usdc balance", usdc.balanceOf(address(this))); + console.log("usdc balance", int256(usdc.balanceOf(address(poolManager))) - int256(initBal)); + console.log("usdc balance", usdc.balanceOf(address(sender))); + console.log("option balance", option.balanceOf(address(sender))); // poolManager.sync(); return data; } - function swap() public { - poolManager.unlock(bytes("")); + function swap(address sender) public { + poolManager.unlock(abi.encode(sender)); } } @@ -94,9 +115,15 @@ contract OpHookTest is Test { address optionAddress; PoolKey public poolKey1; PoolKey public poolKey2; + IPoolManager poolManager; + string MAINNET_RPC_URL = "https://reth-ethereum.ithaca.xyz/rpc"; + uint mainnetFork; function setUp() public { + mainnetFork = vm.createSelectFork(MAINNET_RPC_URL, 23359458); + poolManager = IPoolManager(ConstantsMainnet.POOLMANAGER); + deal(address(this), 10000e20 ether); deal(ConstantsMainnet.USDC, address(this), 1000e6); // Deploy mock tokens @@ -142,6 +169,7 @@ contract OpHookTest is Test { deal(address(weth), address(opHook), 1000e18); + deal(address(usdc), address(opHook), 1000e18); usdc.approve(address(opHook), 1000e6); usdc.approve(ConstantsMainnet.POOLMANAGER, 1000e6); usdc.approve(ConstantsMainnet.PERMIT2, 1000e6); @@ -152,21 +180,67 @@ contract OpHookTest is Test { // } function testSwapCallback() public { - IPoolManager poolManager = IPoolManager(ConstantsMainnet.POOLMANAGER); + // UniversalRouter router = UniversalRouter(payable(ConstantsMainnet.UNIVERSALROUTER)); SwapCallback swapCallback = new SwapCallback(poolManager, opHook, poolKey1); address swapcb = address(swapCallback); - deal(ConstantsMainnet.USDC, swapcb, 1000e6); - swapCallback.swap(); - console.log("option1 balance", option1.balanceOf(swapcb)); + deal(address(usdc), swapcb, 1000e18); + deal(address(usdc), address(this), 1000e18); + usdc.approve(ConstantsMainnet.PERMIT2, 1000e6); + usdc.approve(swapcb, 1000e6); + usdc.approve(ConstantsMainnet.POOLMANAGER, 1000e6); + swapCallback.swap(address(this)); + + } + + function testRouterSwap() public { + UniversalRouter router = UniversalRouter(payable(ConstantsMainnet.UNIVERSALROUTER)); + deal(ConstantsMainnet.USDC, address(this), 1000e6); + usdc.approve(address(router), 1000e6); + usdc.approve(address(poolManager), 1000e6); + usdc.approve(ConstantsMainnet.PERMIT2, 1000e6); + + IPermit2 permit2 = IPermit2(ConstantsMainnet.PERMIT2); + permit2.approve(address(usdc), address(router), type(uint160).max, uint48(block.timestamp + 1 days)); + permit2.approve(address(usdc), address(poolManager), type(uint160).max, uint48(block.timestamp + 1 days)); + + // currency0 = option, currency1 = usdc + + uint256 V4_SWAP = 0x10; + + bytes memory commands = abi.encodePacked(uint8(V4_SWAP)); + bytes memory actions = abi.encodePacked( + uint8(Actions.SWAP_EXACT_IN_SINGLE), + uint8(Actions.SETTLE_ALL), + uint8(Actions.TAKE_ALL) + ); + + bytes[] memory params = new bytes[](3); + + params[0] = abi.encode( + IV4Router.ExactInputSingleParams({ + poolKey: poolKey1, + zeroForOne: false, + amountIn: 1e6, + amountOutMinimum: 0, + hookData: bytes("") + }) + ); + params[1] = abi.encode(poolKey1.currency1, type(uint256).max); + params[2] = abi.encode(poolKey1.currency0, 0); + + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(actions, params); + + router.execute(commands, inputs, block.timestamp + 20); + + console.log("option1 balance", option1.balanceOf(address(this))); console.log("option1 balance", option1.balanceOf(address(opHook))); - console.log("option2 balance", option2.balanceOf(address(this))); - console.log("WETH balance", weth.balanceOf(address(this))); console.log("WETH balance", weth.balanceOf(address(opHook))); console.log("USDC balance", usdc.balanceOf(address(this))); console.log("USDC balance", usdc.balanceOf(address(opHook))); - console.log("USDC balance", usdc.balanceOf(swapcb)); - console.log("ophookaddress", address(opHook)); + console.log("USDC balance", usdc.balanceOf(address(this))); + console.log("USDC balance", usdc.balanceOf(address(poolManager))); } // function testGetUnderlyingPrice() public view { diff --git a/packages/foundry/test/SafeCallback.sol b/packages/foundry/test/SafeCallback.sol index 72213be..5ff070c 100644 --- a/packages/foundry/test/SafeCallback.sol +++ b/packages/foundry/test/SafeCallback.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.26; -import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol"; -import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; +import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; /// @title IImmutableState /// @notice Interface for the ImmutableState contract diff --git a/packages/foundry/test/UniHook.t.sol b/packages/foundry/test/UniHook.t.sol index 60f99a8..708388c 100644 --- a/packages/foundry/test/UniHook.t.sol +++ b/packages/foundry/test/UniHook.t.sol @@ -6,9 +6,9 @@ import "forge-std/console.sol"; import "../contracts/OpHook.sol"; import "../contracts/IOptionToken.sol"; import "../contracts/MockOptionToken.sol"; -import {HookMiner} from "lib/uniswap-hooks/lib/v4-periphery/src/utils/HookMiner.sol"; -import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; +import {HookMiner} from "@uniswap/v4-periphery/src/utils/HookMiner.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IWETH9} from "lib/v4-periphery/src/interfaces/external/IWETH9.sol"; @@ -16,12 +16,12 @@ import {IWETH9} from "lib/v4-periphery/src/interfaces/external/IWETH9.sol"; import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol"; -import {Hooks} from "v4-core/src/libraries/Hooks.sol"; -import {IPoolManager, SwapParams} from "v4-core/src/interfaces/IPoolManager.sol"; -import {PoolKey} from "v4-core/src/types/PoolKey.sol"; -import {PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; -import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/src/types/BeforeSwapDelta.sol"; -import {Currency} from "v4-core/src/types/Currency.sol"; +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; +import {IPoolManager, SwapParams} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {BeforeSwapDelta, toBeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; @@ -37,7 +37,7 @@ import {OptionPrice, IUniswapV3Pool} from "../contracts/OptionPrice.sol"; import {IOptionToken} from "../contracts/IOptionToken.sol"; import {IPermit2} from "../contracts/IPermit2.sol"; -import {IHooks} from "v4-core/src/interfaces/IHooks.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; contract UniOpHookTest is Test { // using EasyPosm for IPositionManager;