From b84c13e42e754c0948871b907708e947f709c1ad Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 14:27:35 -0400 Subject: [PATCH 01/17] initial, unoptimized, hook mining library --- test/utils/HookMiner.sol | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/utils/HookMiner.sol diff --git a/test/utils/HookMiner.sol b/test/utils/HookMiner.sol new file mode 100644 index 00000000..79d8ec3f --- /dev/null +++ b/test/utils/HookMiner.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.19; + +/// @title HookMiner - a library for mining hook addresses +/// @dev This library is intended for `forge test` environments. There may be gotchas when using salts in `forge script` or `forge create` +library HookMiner { + // mask to slice out the top 8 bit of the address + uint160 constant FLAG_MASK = 0xFF << 152; + + // Maximum number of iterations to find a salt, avoid infinite loops + uint256 constant MAX_LOOP = 10_000; + + /// @notice Find a salt that produces a hook address with the desired `flags` + /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address + /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) + /// @param flags The desired flags for the hook address + /// @param seed Use 0 for as a default. An optional starting salt when linearly searching for a salt. Useful for finding salts for multiple hooks with the same flags + /// @param creationCode The creation code of a hook contract. Example: `abi.encodePacked(type(Counter).creationCode, abi.encode())` + /// @return hookAddress the salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}()` + function find(address deployer, uint160 flags, uint256 seed, bytes memory creationCode) + external + pure + returns (address hookAddress, bytes32 salt) + { + uint160 prefix; + uint256 i = seed; + for (i; i < MAX_LOOP;) { + hookAddress = computeAddress(deployer, salt, creationCode); + prefix = uint160(hookAddress) & FLAG_MASK; + if (prefix == flags) { + break; + } + + unchecked { + ++i; + } + } + require(uint160(hookAddress) & FLAG_MASK == flags, "HookMiner: could not find hook address"); + } + + /// @notice Precompute a contract address deployed via CREATE2 + /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address + /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) + /// @param salt The salt used to deploy the hook + /// @param creationCode The creation code of a hook contract + function computeAddress(address deployer, bytes32 salt, bytes memory creationCode) + public + pure + returns (address hookAddress) + { + return address( + uint160(uint256(keccak256(abi.encodePacked(bytes1(0xFF), deployer, salt, keccak256(creationCode))))) + ); + } +} From 1d4fb2bf25b2e81eb2755057c1ad45489fd4f01d Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 14:38:50 -0400 Subject: [PATCH 02/17] minor fixes --- test/utils/HookMiner.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/utils/HookMiner.sol b/test/utils/HookMiner.sol index 79d8ec3f..59b4d9cd 100644 --- a/test/utils/HookMiner.sol +++ b/test/utils/HookMiner.sol @@ -25,7 +25,7 @@ library HookMiner { uint160 prefix; uint256 i = seed; for (i; i < MAX_LOOP;) { - hookAddress = computeAddress(deployer, salt, creationCode); + hookAddress = computeAddress(deployer, i, creationCode); prefix = uint160(hookAddress) & FLAG_MASK; if (prefix == flags) { break; @@ -35,6 +35,7 @@ library HookMiner { ++i; } } + salt = bytes32(i); require(uint160(hookAddress) & FLAG_MASK == flags, "HookMiner: could not find hook address"); } @@ -43,7 +44,7 @@ library HookMiner { /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) /// @param salt The salt used to deploy the hook /// @param creationCode The creation code of a hook contract - function computeAddress(address deployer, bytes32 salt, bytes memory creationCode) + function computeAddress(address deployer, uint256 salt, bytes memory creationCode) public pure returns (address hookAddress) From 3679d17299f1737c3a13bc25562c6320594e7949 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 14:42:11 -0400 Subject: [PATCH 03/17] replace implementation+etch with CREATE2 instead --- test/TWAMM.t.sol | 26 +++++++------------ .../implementation/TWAMMImplementation.sol | 16 ------------ 2 files changed, 10 insertions(+), 32 deletions(-) delete mode 100644 test/shared/implementation/TWAMMImplementation.sol diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index 7aef0156..0e3f6fbd 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -5,7 +5,6 @@ import {Vm} from "forge-std/Vm.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {MockERC20} from "@uniswap/v4-core/test/foundry-tests/utils/MockERC20.sol"; import {IERC20Minimal} from "@uniswap/v4-core/contracts/interfaces/external/IERC20Minimal.sol"; -import {TWAMMImplementation} from "./shared/implementation/TWAMMImplementation.sol"; import {IHooks} from "@uniswap/v4-core/contracts/interfaces/IHooks.sol"; import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; import {TickMath} from "@uniswap/v4-core/contracts/libraries/TickMath.sol"; @@ -21,6 +20,7 @@ import {CurrencyLibrary, Currency} from "@uniswap/v4-core/contracts/types/Curren import {TWAMM} from "../contracts/hooks/examples/TWAMM.sol"; import {ITWAMM} from "../contracts/interfaces/ITWAMM.sol"; import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol"; +import {HookMiner} from "./utils/HookMiner.sol"; contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { using PoolIdLibrary for PoolKey; @@ -44,11 +44,7 @@ contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { uint256 earningsFactorLast ); - // address constant TWAMMAddr = address(uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG)); - TWAMM twamm = TWAMM( - address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG)) - ); - // TWAMM twamm; + TWAMM twamm; PoolManager manager; PoolModifyPositionTest modifyPositionRouter; PoolSwapTest swapRouter; @@ -65,16 +61,14 @@ contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { token1 = MockERC20(Currency.unwrap(currency1)); manager = new PoolManager(500000); - TWAMMImplementation impl = new TWAMMImplementation(manager, 10_000, twamm); - (, bytes32[] memory writes) = vm.accesses(address(impl)); - vm.etch(address(twamm), address(impl).code); - // for each storage key that was written during the hook implementation, copy the value over - unchecked { - for (uint256 i = 0; i < writes.length; i++) { - bytes32 slot = writes[i]; - vm.store(address(twamm), slot, vm.load(address(impl), slot)); - } - } + // Find a salt that produces a hook address with the desired `flags` + uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG); + bytes memory creationCode = abi.encodePacked(type(TWAMM).creationCode, abi.encode(address(manager), 10_000)); + (address hookAddress, bytes32 salt) = HookMiner.find(address(this), flags, 0, creationCode); + + // Deploy hook to the precomputed address using the salt + twamm = new TWAMM{salt: salt}(IPoolManager(address(manager)), 10_000); + require(address(twamm) == hookAddress, "TWAMMTest: hook address does not match"); // safety check that salt was mined correctly modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); swapRouter = new PoolSwapTest(IPoolManager(address(manager))); diff --git a/test/shared/implementation/TWAMMImplementation.sol b/test/shared/implementation/TWAMMImplementation.sol deleted file mode 100644 index 012ca541..00000000 --- a/test/shared/implementation/TWAMMImplementation.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {BaseHook} from "../../../contracts/BaseHook.sol"; -import {TWAMM} from "../../../contracts/hooks/examples/TWAMM.sol"; -import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; -import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; - -contract TWAMMImplementation is TWAMM { - constructor(IPoolManager poolManager, uint256 interval, TWAMM addressToEtch) TWAMM(poolManager, interval) { - Hooks.validateHookAddress(addressToEtch, getHooksCalls()); - } - - // make this a no-op in testing - function validateHookAddress(BaseHook _this) internal pure override {} -} From f691b469cd1256154e9063d7d20ee969d875425e Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:07:09 -0400 Subject: [PATCH 04/17] abstract creation code packing to the library --- test/TWAMM.t.sol | 11 ++++++----- test/utils/HookMiner.sol | 18 +++++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index 0e3f6fbd..23274b12 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -62,13 +62,14 @@ contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { manager = new PoolManager(500000); // Find a salt that produces a hook address with the desired `flags` - uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG); - bytes memory creationCode = abi.encodePacked(type(TWAMM).creationCode, abi.encode(address(manager), 10_000)); - (address hookAddress, bytes32 salt) = HookMiner.find(address(this), flags, 0, creationCode); - + uint160 flags = + uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG); + (address hookAddress, bytes32 salt) = + HookMiner.find(address(this), flags, 0, type(TWAMM).creationCode, abi.encode(address(manager), 10_000)); + // Deploy hook to the precomputed address using the salt twamm = new TWAMM{salt: salt}(IPoolManager(address(manager)), 10_000); - require(address(twamm) == hookAddress, "TWAMMTest: hook address does not match"); // safety check that salt was mined correctly + require(address(twamm) == hookAddress, "TWAMMTest: hook address does not match"); // safety check that salt was mined correctly modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); swapRouter = new PoolSwapTest(IPoolManager(address(manager))); diff --git a/test/utils/HookMiner.sol b/test/utils/HookMiner.sol index 59b4d9cd..5b55e982 100644 --- a/test/utils/HookMiner.sol +++ b/test/utils/HookMiner.sol @@ -15,17 +15,21 @@ library HookMiner { /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) /// @param flags The desired flags for the hook address /// @param seed Use 0 for as a default. An optional starting salt when linearly searching for a salt. Useful for finding salts for multiple hooks with the same flags - /// @param creationCode The creation code of a hook contract. Example: `abi.encodePacked(type(Counter).creationCode, abi.encode())` + /// @param creationCode The creation code of a hook contract. Example: `type(Counter).creationCode` + /// @param constructorArgs The encoded constructor arguments of a hook contract. Example: `abi.encode(address(manager))` /// @return hookAddress the salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}()` - function find(address deployer, uint160 flags, uint256 seed, bytes memory creationCode) - external - pure - returns (address hookAddress, bytes32 salt) - { + function find( + address deployer, + uint160 flags, + uint256 seed, + bytes memory creationCode, + bytes memory constructorArgs + ) external pure returns (address hookAddress, bytes32 salt) { + bytes memory creationCodeWithArgs = abi.encodePacked(creationCode, constructorArgs); uint160 prefix; uint256 i = seed; for (i; i < MAX_LOOP;) { - hookAddress = computeAddress(deployer, i, creationCode); + hookAddress = computeAddress(deployer, i, creationCodeWithArgs); prefix = uint160(hookAddress) & FLAG_MASK; if (prefix == flags) { break; From 2a87d5eb5f0b63ab367b8963b7f88ef09d06e13d Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:09:14 -0400 Subject: [PATCH 05/17] add forge-gas-snapshot --- remappings.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/remappings.txt b/remappings.txt index e05c5bd6..7fee2a9a 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,3 +2,4 @@ solmate/=lib/solmate/src/ forge-std/=lib/forge-std/src/ @openzeppelin/=lib/openzeppelin-contracts/ +forge-gas-snapshot/=lib/forge-gas-snapshot/src/ From 1b285f3bedf1e1d237313b74278b7dbf7fd14533 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:12:49 -0400 Subject: [PATCH 06/17] LimitOrder: replace implementation+etch with CREATE2 instead --- test/LimitOrder.t.sol | 20 +++++++------------ .../LimitOrderImplementation.sol | 16 --------------- 2 files changed, 7 insertions(+), 29 deletions(-) delete mode 100644 test/shared/implementation/LimitOrderImplementation.sol diff --git a/test/LimitOrder.t.sol b/test/LimitOrder.t.sol index 9939c7e2..0851d5d0 100644 --- a/test/LimitOrder.t.sol +++ b/test/LimitOrder.t.sol @@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol"; import {GetSender} from "./shared/GetSender.sol"; import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; import {LimitOrder, Epoch, EpochLibrary} from "../contracts/hooks/examples/LimitOrder.sol"; -import {LimitOrderImplementation} from "./shared/implementation/LimitOrderImplementation.sol"; import {PoolManager} from "@uniswap/v4-core/contracts/PoolManager.sol"; import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; import {Deployers} from "@uniswap/v4-core/test/foundry-tests/utils/Deployers.sol"; @@ -16,6 +15,7 @@ import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/contracts/types/PoolId.sol import {PoolSwapTest} from "@uniswap/v4-core/contracts/test/PoolSwapTest.sol"; import {TickMath} from "@uniswap/v4-core/contracts/libraries/TickMath.sol"; import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol"; +import {HookMiner} from "./utils/HookMiner.sol"; contract TestLimitOrder is Test, Deployers, TokenFixture { using PoolIdLibrary for PoolKey; @@ -25,7 +25,7 @@ contract TestLimitOrder is Test, Deployers, TokenFixture { TestERC20 token0; TestERC20 token1; PoolManager manager; - LimitOrder limitOrder = LimitOrder(address(uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_SWAP_FLAG))); + LimitOrder limitOrder; PoolKey key; PoolId id; @@ -38,17 +38,11 @@ contract TestLimitOrder is Test, Deployers, TokenFixture { manager = new PoolManager(500000); - vm.record(); - LimitOrderImplementation impl = new LimitOrderImplementation(manager, limitOrder); - (, bytes32[] memory writes) = vm.accesses(address(impl)); - vm.etch(address(limitOrder), address(impl).code); - // for each storage key that was written during the hook implementation, copy the value over - unchecked { - for (uint256 i = 0; i < writes.length; i++) { - bytes32 slot = writes[i]; - vm.store(address(limitOrder), slot, vm.load(address(impl), slot)); - } - } + uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_SWAP_FLAG); + (address hookAddress, bytes32 salt) = + HookMiner.find(address(this), flags, 0, type(LimitOrder).creationCode, abi.encode(manager)); + limitOrder = new LimitOrder{salt: salt}(manager); + require(address(limitOrder) == hookAddress, "TestLimitOrder: hook address mismatch"); key = PoolKey(currency0, currency1, 3000, 60, limitOrder); id = key.toId(); diff --git a/test/shared/implementation/LimitOrderImplementation.sol b/test/shared/implementation/LimitOrderImplementation.sol deleted file mode 100644 index 340cfc42..00000000 --- a/test/shared/implementation/LimitOrderImplementation.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {BaseHook} from "../../../contracts/BaseHook.sol"; -import {LimitOrder} from "../../../contracts/hooks/examples/LimitOrder.sol"; -import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; -import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; - -contract LimitOrderImplementation is LimitOrder { - constructor(IPoolManager _poolManager, LimitOrder addressToEtch) LimitOrder(_poolManager) { - Hooks.validateHookAddress(addressToEtch, getHooksCalls()); - } - - // make this a no-op in testing - function validateHookAddress(BaseHook _this) internal pure override {} -} From d0e83da853e4a1bbf9c92321e3c8c95f0b0df41b Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:14:38 -0400 Subject: [PATCH 07/17] remove verbose type casting --- test/TWAMM.t.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index 23274b12..476eb0c3 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -49,7 +49,6 @@ contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { PoolModifyPositionTest modifyPositionRouter; PoolSwapTest swapRouter; PoolDonateTest donateRouter; - address hookAddress; MockERC20 token0; MockERC20 token1; PoolKey poolKey; @@ -65,10 +64,10 @@ contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG); (address hookAddress, bytes32 salt) = - HookMiner.find(address(this), flags, 0, type(TWAMM).creationCode, abi.encode(address(manager), 10_000)); + HookMiner.find(address(this), flags, 0, type(TWAMM).creationCode, abi.encode(manager, 10_000)); // Deploy hook to the precomputed address using the salt - twamm = new TWAMM{salt: salt}(IPoolManager(address(manager)), 10_000); + twamm = new TWAMM{salt: salt}(manager, 10_000); require(address(twamm) == hookAddress, "TWAMMTest: hook address does not match"); // safety check that salt was mined correctly modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); From df57112496c63ef67145e73a0b5d0f294b01b833 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:17:52 -0400 Subject: [PATCH 08/17] FullRange: replace implementation+etch with CREATE2 instead --- .forge-snapshots/FullRangeFirstSwap.snap | 2 +- .forge-snapshots/FullRangeSecondSwap.snap | 2 +- .forge-snapshots/FullRangeSwap.snap | 2 +- test/FullRange.t.sol | 14 ++++++++------ .../implementation/FullRangeImplementation.sol | 16 ---------------- 5 files changed, 11 insertions(+), 25 deletions(-) delete mode 100644 test/shared/implementation/FullRangeImplementation.sol diff --git a/.forge-snapshots/FullRangeFirstSwap.snap b/.forge-snapshots/FullRangeFirstSwap.snap index 47ea1890..5eac5def 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -153143 \ No newline at end of file +153142 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index cadd0b38..50511058 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -110682 \ No newline at end of file +110681 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSwap.snap b/.forge-snapshots/FullRangeSwap.snap index 7233e853..ec91169c 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -151418 \ No newline at end of file +151417 \ No newline at end of file diff --git a/test/FullRange.t.sol b/test/FullRange.t.sol index 8cf9bb3d..ae25524b 100644 --- a/test/FullRange.t.sol +++ b/test/FullRange.t.sol @@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; import {FullRange} from "../contracts/hooks/examples/FullRange.sol"; -import {FullRangeImplementation} from "./shared/implementation/FullRangeImplementation.sol"; import {PoolManager} from "@uniswap/v4-core/contracts/PoolManager.sol"; import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; import {Deployers} from "@uniswap/v4-core/test/foundry-tests/utils/Deployers.sol"; @@ -19,6 +18,7 @@ import {IHooks} from "@uniswap/v4-core/contracts/interfaces/IHooks.sol"; import {UniswapV4ERC20} from "../contracts/libraries/UniswapV4ERC20.sol"; import {FullMath} from "@uniswap/v4-core/contracts/libraries/FullMath.sol"; import {SafeCast} from "@uniswap/v4-core/contracts/libraries/SafeCast.sol"; +import {HookMiner} from "./utils/HookMiner.sol"; contract TestFullRange is Test, Deployers, GasSnapshot { using PoolIdLibrary for PoolKey; @@ -66,9 +66,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { Currency currency1; PoolManager manager; - FullRangeImplementation fullRange = FullRangeImplementation( - address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.BEFORE_SWAP_FLAG)) - ); + FullRange fullRange; PoolKey key; PoolId id; @@ -90,8 +88,12 @@ contract TestFullRange is Test, Deployers, GasSnapshot { manager = new PoolManager(500000); - FullRangeImplementation impl = new FullRangeImplementation(manager, fullRange); - vm.etch(address(fullRange), address(impl).code); + uint160 flags = + uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.BEFORE_SWAP_FLAG); + (address hookAddress, bytes32 salt) = + HookMiner.find(address(this), flags, 0, type(FullRange).creationCode, abi.encode(manager)); + fullRange = new FullRange{salt: salt}(manager); + require(address(fullRange) == hookAddress, "TestFullRange: hook address mismatch"); key = createPoolKey(token0, token1); id = key.toId(); diff --git a/test/shared/implementation/FullRangeImplementation.sol b/test/shared/implementation/FullRangeImplementation.sol deleted file mode 100644 index fcd8ae3f..00000000 --- a/test/shared/implementation/FullRangeImplementation.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {BaseHook} from "../../../contracts/BaseHook.sol"; -import {FullRange} from "../../../contracts/hooks/examples/FullRange.sol"; -import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; -import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; - -contract FullRangeImplementation is FullRange { - constructor(IPoolManager _poolManager, FullRange addressToEtch) FullRange(_poolManager) { - Hooks.validateHookAddress(addressToEtch, getHooksCalls()); - } - - // make this a no-op in testing - function validateHookAddress(BaseHook _this) internal pure override {} -} From ccd3ea161198007c49eac0511bd3ef58daa003f9 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:27:24 -0400 Subject: [PATCH 09/17] GeomeanOracle: replace implementation+etch with CREATE2 instead; use vm cheatcodes for advancing block.timestamp --- test/GeomeanOracle.t.sol | 36 +++++++------------ .../GeomeanOracleImplementation.sol | 26 -------------- 2 files changed, 13 insertions(+), 49 deletions(-) delete mode 100644 test/shared/implementation/GeomeanOracleImplementation.sol diff --git a/test/GeomeanOracle.t.sol b/test/GeomeanOracle.t.sol index 523498bf..1e45f98e 100644 --- a/test/GeomeanOracle.t.sol +++ b/test/GeomeanOracle.t.sol @@ -5,7 +5,6 @@ import {Test} from "forge-std/Test.sol"; import {GetSender} from "./shared/GetSender.sol"; import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; import {GeomeanOracle} from "../contracts/hooks/examples/GeomeanOracle.sol"; -import {GeomeanOracleImplementation} from "./shared/implementation/GeomeanOracleImplementation.sol"; import {PoolManager} from "@uniswap/v4-core/contracts/PoolManager.sol"; import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; import {Deployers} from "@uniswap/v4-core/test/foundry-tests/utils/Deployers.sol"; @@ -17,6 +16,7 @@ import {PoolModifyPositionTest} from "@uniswap/v4-core/contracts/test/PoolModify import {TickMath} from "@uniswap/v4-core/contracts/libraries/TickMath.sol"; import {Oracle} from "../contracts/libraries/Oracle.sol"; import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol"; +import {HookMiner} from "./utils/HookMiner.sol"; contract TestGeomeanOracle is Test, Deployers, TokenFixture { using PoolIdLibrary for PoolKey; @@ -27,14 +27,7 @@ contract TestGeomeanOracle is Test, Deployers, TokenFixture { TestERC20 token0; TestERC20 token1; PoolManager manager; - GeomeanOracleImplementation geomeanOracle = GeomeanOracleImplementation( - address( - uint160( - Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG - | Hooks.BEFORE_SWAP_FLAG - ) - ) - ); + GeomeanOracle geomeanOracle; PoolKey key; PoolId id; @@ -47,18 +40,15 @@ contract TestGeomeanOracle is Test, Deployers, TokenFixture { manager = new PoolManager(500000); - vm.record(); - GeomeanOracleImplementation impl = new GeomeanOracleImplementation(manager, geomeanOracle); - (, bytes32[] memory writes) = vm.accesses(address(impl)); - vm.etch(address(geomeanOracle), address(impl).code); - // for each storage key that was written during the hook implementation, copy the value over - unchecked { - for (uint256 i = 0; i < writes.length; i++) { - bytes32 slot = writes[i]; - vm.store(address(geomeanOracle), slot, vm.load(address(impl), slot)); - } - } - geomeanOracle.setTime(1); + uint160 flags = uint160( + Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG + | Hooks.BEFORE_SWAP_FLAG + ); + (address hookAddress, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(GeomeanOracle).creationCode, abi.encode(manager)); + geomeanOracle = new GeomeanOracle{salt: salt}(manager); + require(address(geomeanOracle) == hookAddress, "TestGeomeanOracle: hook address mismatch"); + + vm.warp(1); key = PoolKey(currency0, currency1, 0, MAX_TICK_SPACING, geomeanOracle); id = key.toId(); @@ -144,7 +134,7 @@ contract TestGeomeanOracle is Test, Deployers, TokenFixture { function testBeforeModifyPositionObservation() public { manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); - geomeanOracle.setTime(3); // advance 2 seconds + skip(2); // advance 2 seconds modifyPositionRouter.modifyPosition( key, IPoolManager.ModifyPositionParams( @@ -166,7 +156,7 @@ contract TestGeomeanOracle is Test, Deployers, TokenFixture { function testBeforeModifyPositionObservationAndCardinality() public { manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); - geomeanOracle.setTime(3); // advance 2 seconds + skip(2); // advance 2 seconds geomeanOracle.increaseCardinalityNext(key, 2); GeomeanOracle.ObservationState memory observationState = geomeanOracle.getState(key); assertEq(observationState.index, 0); diff --git a/test/shared/implementation/GeomeanOracleImplementation.sol b/test/shared/implementation/GeomeanOracleImplementation.sol deleted file mode 100644 index 06a95fa2..00000000 --- a/test/shared/implementation/GeomeanOracleImplementation.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {BaseHook} from "../../../contracts/BaseHook.sol"; -import {GeomeanOracle} from "../../../contracts/hooks/examples/GeomeanOracle.sol"; -import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; -import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; - -contract GeomeanOracleImplementation is GeomeanOracle { - uint32 public time; - - constructor(IPoolManager _poolManager, GeomeanOracle addressToEtch) GeomeanOracle(_poolManager) { - Hooks.validateHookAddress(addressToEtch, getHooksCalls()); - } - - // make this a no-op in testing - function validateHookAddress(BaseHook _this) internal pure override {} - - function setTime(uint32 _time) external { - time = _time; - } - - function _blockTimestamp() internal view override returns (uint32) { - return time; - } -} From e2834d3e2b80c6b1024515512be7b38e6f0415d0 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:32:34 -0400 Subject: [PATCH 10/17] forge snapshot --- .gas-snapshot | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 .gas-snapshot diff --git a/.gas-snapshot b/.gas-snapshot new file mode 100644 index 00000000..3671cd93 --- /dev/null +++ b/.gas-snapshot @@ -0,0 +1,116 @@ +TWAMMTest:testTWAMMEndToEndSimSymmetricalOrderPools() (gas: 796056) +TWAMMTest:testTWAMM_beforeInitialize_SetsLastVirtualOrderTimestamp() (gas: 1543971) +TWAMMTest:testTWAMM_submitOrder_EmitsEvent() (gas: 176414) +TWAMMTest:testTWAMM_submitOrder_StoresOrderWithCorrectPoolAndOrderPoolInfo() (gas: 218226) +TWAMMTest:testTWAMM_submitOrder_StoresSellRatesEarningsFactorsProperly() (gas: 955160) +TWAMMTest:testTWAMM_updateOrder_EmitsEvent() (gas: 436339) +TWAMMTest:testTWAMM_updateOrder_OneForZero_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514301) +TWAMMTest:testTWAMM_updateOrder_ZeroForOne_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514231) +TWAMMTest:testTWAMM_updatedOrder_OneForZero_ClosesOrderIfEliminatingPosition() (gas: 437734) +TWAMMTest:testTWAMM_updatedOrder_OneForZero_IncreaseOrderAmount() (gas: 502469) +TWAMMTest:testTWAMM_updatedOrder_ZeroForOne_ClosesOrderIfEliminatingPosition() (gas: 437742) +TWAMMTest:testTWAMM_updatedOrder_ZeroForOne_IncreaseOrderAmount() (gas: 502467) +TestFullRange:testFullRange_BeforeModifyPositionFailsWithWrongMsgSender() (gas: 931705) +TestFullRange:testFullRange_addLiquidity_FailsIfNoPool() (gas: 20712) +TestFullRange:testFullRange_addLiquidity_FailsIfTooMuchSlippage() (gas: 1417713) +TestFullRange:testFullRange_addLiquidity_InitialAddFuzz(uint256) (runs: 256, μ: 1040150, ~: 901919) +TestFullRange:testFullRange_addLiquidity_InitialAddSucceeds() (gas: 1287095) +TestFullRange:testFullRange_addLiquidity_SubsequentAdd() (gas: 235736) +TestFullRange:testFullRange_addLiquidity_SwapThenAddSucceeds() (gas: 1471474) +TestFullRange:testFullRange_beforeInitialize_AllowsPoolCreation() (gas: 927663) +TestFullRange:testFullRange_beforeInitialize_RevertsIfWrongSpacing() (gas: 21207) +TestFullRange:testFullRange_removeLiquidity_DiffRatios() (gas: 1405163) +TestFullRange:testFullRange_removeLiquidity_FailsIfNoLiquidity() (gas: 959576) +TestFullRange:testFullRange_removeLiquidity_FailsIfNoPool() (gas: 20523) +TestFullRange:testFullRange_removeLiquidity_InitialRemoveFuzz(uint256) (runs: 256, μ: 1318073, ~: 1328396) +TestFullRange:testFullRange_removeLiquidity_InitialRemoveSucceeds() (gas: 248863) +TestFullRange:testFullRange_removeLiquidity_RemoveAllFuzz(uint256) (runs: 256, μ: 1072322, ~: 905318) +TestFullRange:testFullRange_removeLiquidity_SucceedsWithPartial() (gas: 1341038) +TestFullRange:testFullRange_removeLiquidity_SwapAndRebalance() (gas: 579224) +TestFullRange:testFullRange_removeLiquidity_SwapRemoveAllFuzz(uint256) (runs: 256, μ: 1157950, ~: 905264) +TestFullRange:testFullRange_removeLiquidity_ThreeLPsRemovePrincipalAndFees() (gas: 1917405) +TestFullRange:testFullRange_swap_TwoPools() (gas: 2596836) +TestFullRange:testFullRange_swap_TwoSwaps() (gas: 1430392) +TestGeomeanOracle:testAfterInitializeObservation() (gas: 103263) +TestGeomeanOracle:testAfterInitializeObserve0() (gas: 111551) +TestGeomeanOracle:testAfterInitializeState() (gas: 102994) +TestGeomeanOracle:testBeforeInitializeAllowsPoolCreation() (gas: 97765) +TestGeomeanOracle:testBeforeInitializeRevertsIfFee() (gas: 21174) +TestGeomeanOracle:testBeforeInitializeRevertsIfNotMaxTickSpacing() (gas: 22006) +TestGeomeanOracle:testBeforeModifyPositionNoObservations() (gas: 451900) +TestGeomeanOracle:testBeforeModifyPositionObservation() (gas: 455898) +TestGeomeanOracle:testBeforeModifyPositionObservationAndCardinality() (gas: 487676) +TestLimitOrder:testEpochNext() (gas: 7744) +TestLimitOrder:testGetTickLowerLast() (gas: 9959) +TestLimitOrder:testGetTickLowerLastWithDifferentPrice() (gas: 74899) +TestLimitOrder:testKill() (gas: 505672) +TestLimitOrder:testMultipleLPs() (gas: 611748) +TestLimitOrder:testNotZeroForOneCrossedRangeRevert() (gas: 239469) +TestLimitOrder:testNotZeroForOneInRangeRevert() (gas: 359048) +TestLimitOrder:testNotZeroForOneLeftBoundaryOfCurrentRange() (gas: 391999) +TestLimitOrder:testSwapAcrossRange() (gas: 833945) +TestLimitOrder:testZeroForOneCrossedRangeRevert() (gas: 238259) +TestLimitOrder:testZeroForOneInRangeRevert() (gas: 306791) +TestLimitOrder:testZeroForOneLeftBoundaryOfCurrentRange() (gas: 393271) +TestLimitOrder:testZeroForOneRightBoundaryOfCurrentRange() (gas: 364741) +TestLimitOrder:testZeroLiquidityRevert() (gas: 15562) +TestOracle:testCanFetchMultipleObservations() (gas: 154980) +TestOracle:testDoesNotFailAcrossOverflowBoundary() (gas: 58950) +TestOracle:testFullOracle() (gas: 2090424997) +TestOracle:testFullOracleGasCostObserve0After5Seconds() (gas: 2090269961) +TestOracle:testFullOracleGasCostObserve200By13() (gas: 2090287915) +TestOracle:testFullOracleGasCostObserve200By13Plus5() (gas: 2090288121) +TestOracle:testFullOracleGasCostObserve5After5Seconds() (gas: 2090270060) +TestOracle:testFullOracleGasCostObserveOldest() (gas: 2090287010) +TestOracle:testFullOracleGasCostObserveOldestAfter5Seconds() (gas: 2090288483) +TestOracle:testFullOracleGasCostObserveZero() (gas: 2090268349) +TestOracle:testGas10SlotCardinalityGreater() (gas: 334542) +TestOracle:testGas10Slots() (gas: 266320) +TestOracle:testGas1Slot() (gas: 66176) +TestOracle:testGas1SlotCardinalityGreater() (gas: 134429) +TestOracle:testGasAllOfLast20Seconds() (gas: 262027) +TestOracle:testGasBetweenOldestAndOldestPlusOne() (gas: 182500) +TestOracle:testGasLatestEqual() (gas: 178357) +TestOracle:testGasLatestTransform() (gas: 180008) +TestOracle:testGasMiddle() (gas: 182642) +TestOracle:testGasOldest() (gas: 182152) +TestOracle:testGrow() (gas: 119740) +TestOracle:testGrowAfterWrap() (gas: 73952) +TestOracle:testInitialize() (gas: 89619) +TestOracle:testInterpolatesAcrossChunkBoundaries() (gas: 110412) +TestOracle:testInterpolatesSame0And1Liquidity() (gas: 108299) +TestOracle:testInterpolationMaxLiquidity() (gas: 108286) +TestOracle:testManyObservationsBetweenLatestObservationAfterLatest(uint32) (runs: 256, μ: 178510, ~: 178510) +TestOracle:testManyObservationsBetweenLatestObservationAtLatest(uint32) (runs: 256, μ: 177475, ~: 177475) +TestOracle:testManyObservationsCurrentObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174759, ~: 174759) +TestOracle:testManyObservationsFetchManyValues(uint32) (runs: 256, μ: 202292, ~: 202292) +TestOracle:testManyObservationsLatestObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174860, ~: 174860) +TestOracle:testManyObservationsLatestObservationSameTimeAsLatest(uint32) (runs: 256, μ: 173207, ~: 173207) +TestOracle:testManyObservationsOlderThanOldestReverts(uint32) (runs: 256, μ: 189853, ~: 189853) +TestOracle:testManyObservationsOldest(uint32) (runs: 256, μ: 176962, ~: 176962) +TestOracle:testManyObservationsOldestAfterTime(uint32) (runs: 256, μ: 178018, ~: 178018) +TestOracle:testManyObservationsSimpleReads(uint32) (runs: 256, μ: 170946, ~: 170946) +TestOracle:testObserveFailsBeforeInitialize() (gas: 14075) +TestOracle:testObserveFailsIfOlderDoesNotExist() (gas: 61464) +TestOracle:testObserveGasCurrentTime() (gas: 62111) +TestOracle:testObserveGasCurrentTimeCounterfactual() (gas: 72518) +TestOracle:testObserveGasSinceMostRecent() (gas: 64214) +TestOracle:testSingleObservationAtCurrentTime() (gas: 56702) +TestOracle:testSingleObservationInPastCounterfactualInPast() (gas: 58870) +TestOracle:testSingleObservationInPastCounterfactualNow() (gas: 58212) +TestOracle:testSingleObservationInRecentPast() (gas: 62566) +TestOracle:testSingleObservationSecondsAgo() (gas: 58334) +TestOracle:testTwoObservationsChronologicalSecondsAgoBetween() (gas: 88680) +TestOracle:testTwoObservationsChronologicalSecondsAgoExactlyFirstObservation() (gas: 88383) +TestOracle:testTwoObservationsChronologicalZeroSecondsAgoCounterfactual() (gas: 86168) +TestOracle:testTwoObservationsChronologicalZeroSecondsAgoExact() (gas: 84658) +TestOracle:testTwoObservationsReverseOrderSecondsAgoBetween() (gas: 93005) +TestOracle:testTwoObservationsReverseOrderSecondsAgoExactlyOnFirstObservation() (gas: 92709) +TestOracle:testTwoObservationsReverseOrderZeroSecondsAgoCounterfactual() (gas: 90537) +TestOracle:testTwoObservationsReverseOrderZeroSecondsAgoExact() (gas: 88961) +TestOracle:testWrite() (gas: 36417) +TestOracle:testWriteAccumulatesLiquidity() (gas: 103839) +TestOracle:testWriteAddsNothingIfTimeUnchanged() (gas: 44663) +TestOracle:testWriteGrowsCardinalityWritingPast() (gas: 95433) +TestOracle:testWriteTimeChanged() (gas: 70010) +TestOracle:testWriteWrapsAround() (gas: 76186) \ No newline at end of file From 9d5eae4656c7339a7d4146c68f242b0d71988511 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:38:15 -0400 Subject: [PATCH 11/17] checkpointing gas-report --- .gas-report | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 .gas-report diff --git a/.gas-report b/.gas-report new file mode 100644 index 00000000..f01561b4 --- /dev/null +++ b/.gas-report @@ -0,0 +1,307 @@ +Compiling 5 files with 0.8.20 +Solc 0.8.20 finished in 6.68s +Compiler run successful! + +Running 9 tests for test/GeomeanOracle.t.sol:TestGeomeanOracle +[PASS] testAfterInitializeObservation() (gas: 103263) +[PASS] testAfterInitializeObserve0() (gas: 111551) +[PASS] testAfterInitializeState() (gas: 102994) +[PASS] testBeforeInitializeAllowsPoolCreation() (gas: 97765) +[PASS] testBeforeInitializeRevertsIfFee() (gas: 21174) +[PASS] testBeforeInitializeRevertsIfNotMaxTickSpacing() (gas: 22006) +[PASS] testBeforeModifyPositionNoObservations() (gas: 451900) +[PASS] testBeforeModifyPositionObservation() (gas: 455898) +[PASS] testBeforeModifyPositionObservationAndCardinality() (gas: 487676) +Test result: ok. 9 passed; 0 failed; 0 skipped; finished in 18.47ms + +Running 12 tests for test/TWAMM.t.sol:TWAMMTest +[PASS] testTWAMMEndToEndSimSymmetricalOrderPools() (gas: 796056) +[PASS] testTWAMM_beforeInitialize_SetsLastVirtualOrderTimestamp() (gas: 1543971) +[PASS] testTWAMM_submitOrder_EmitsEvent() (gas: 176414) +[PASS] testTWAMM_submitOrder_StoresOrderWithCorrectPoolAndOrderPoolInfo() (gas: 218226) +[PASS] testTWAMM_submitOrder_StoresSellRatesEarningsFactorsProperly() (gas: 955160) +[PASS] testTWAMM_updateOrder_EmitsEvent() (gas: 436339) +[PASS] testTWAMM_updateOrder_OneForZero_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514301) +[PASS] testTWAMM_updateOrder_ZeroForOne_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514231) +[PASS] testTWAMM_updatedOrder_OneForZero_ClosesOrderIfEliminatingPosition() (gas: 437734) +[PASS] testTWAMM_updatedOrder_OneForZero_IncreaseOrderAmount() (gas: 502469) +[PASS] testTWAMM_updatedOrder_ZeroForOne_ClosesOrderIfEliminatingPosition() (gas: 437742) +[PASS] testTWAMM_updatedOrder_ZeroForOne_IncreaseOrderAmount() (gas: 502467) +Test result: ok. 12 passed; 0 failed; 0 skipped; finished in 67.81ms + +Running 21 tests for test/FullRange.t.sol:TestFullRange +[PASS] testFullRange_BeforeModifyPositionFailsWithWrongMsgSender() (gas: 931705) +[PASS] testFullRange_addLiquidity_FailsIfNoPool() (gas: 20712) +[PASS] testFullRange_addLiquidity_FailsIfTooMuchSlippage() (gas: 1417713) +[PASS] testFullRange_addLiquidity_InitialAddFuzz(uint256) (runs: 256, μ: 1067175, ~: 901919) +[PASS] testFullRange_addLiquidity_InitialAddSucceeds() (gas: 1287095) +[PASS] testFullRange_addLiquidity_SubsequentAdd() (gas: 235736) +[PASS] testFullRange_addLiquidity_SwapThenAddSucceeds() (gas: 1471474) +[PASS] testFullRange_beforeInitialize_AllowsPoolCreation() (gas: 927663) +[PASS] testFullRange_beforeInitialize_RevertsIfWrongSpacing() (gas: 21207) +[PASS] testFullRange_removeLiquidity_DiffRatios() (gas: 1405163) +[PASS] testFullRange_removeLiquidity_FailsIfNoLiquidity() (gas: 959576) +[PASS] testFullRange_removeLiquidity_FailsIfNoPool() (gas: 20523) +[PASS] testFullRange_removeLiquidity_InitialRemoveFuzz(uint256) (runs: 256, μ: 1319139, ~: 1334491) +[PASS] testFullRange_removeLiquidity_InitialRemoveSucceeds() (gas: 248863) +[PASS] testFullRange_removeLiquidity_RemoveAllFuzz(uint256) (runs: 256, μ: 1082176, ~: 905318) +[PASS] testFullRange_removeLiquidity_SucceedsWithPartial() (gas: 1341038) +[PASS] testFullRange_removeLiquidity_SwapAndRebalance() (gas: 579224) +[PASS] testFullRange_removeLiquidity_SwapRemoveAllFuzz(uint256) (runs: 256, μ: 1190490, ~: 905264) +[PASS] testFullRange_removeLiquidity_ThreeLPsRemovePrincipalAndFees() (gas: 1917405) +[PASS] testFullRange_swap_TwoPools() (gas: 2596836) +[PASS] testFullRange_swap_TwoSwaps() (gas: 1430392) +Test result: ok. 21 passed; 0 failed; 0 skipped; finished in 1.39s + +Running 14 tests for test/LimitOrder.t.sol:TestLimitOrder +[PASS] testEpochNext() (gas: 7744) +[PASS] testGetTickLowerLast() (gas: 9959) +[PASS] testGetTickLowerLastWithDifferentPrice() (gas: 74899) +[PASS] testKill() (gas: 505672) +[PASS] testMultipleLPs() (gas: 611748) +[PASS] testNotZeroForOneCrossedRangeRevert() (gas: 239469) +[PASS] testNotZeroForOneInRangeRevert() (gas: 359048) +[PASS] testNotZeroForOneLeftBoundaryOfCurrentRange() (gas: 391999) +[PASS] testSwapAcrossRange() (gas: 833945) +[PASS] testZeroForOneCrossedRangeRevert() (gas: 238259) +[PASS] testZeroForOneInRangeRevert() (gas: 306791) +[PASS] testZeroForOneLeftBoundaryOfCurrentRange() (gas: 393271) +[PASS] testZeroForOneRightBoundaryOfCurrentRange() (gas: 364741) +[PASS] testZeroLiquidityRevert() (gas: 15562) +Test result: ok. 14 passed; 0 failed; 0 skipped; finished in 2.24s + +Running 60 tests for test/Oracle.t.sol:TestOracle +[PASS] testCanFetchMultipleObservations() (gas: 154980) +[PASS] testDoesNotFailAcrossOverflowBoundary() (gas: 58950) +[PASS] testFullOracle() (gas: 2090424997) +[PASS] testFullOracleGasCostObserve0After5Seconds() (gas: 2090269961) +[PASS] testFullOracleGasCostObserve200By13() (gas: 2090287915) +[PASS] testFullOracleGasCostObserve200By13Plus5() (gas: 2090288121) +[PASS] testFullOracleGasCostObserve5After5Seconds() (gas: 2090270060) +[PASS] testFullOracleGasCostObserveOldest() (gas: 2090287010) +[PASS] testFullOracleGasCostObserveOldestAfter5Seconds() (gas: 2090288483) +[PASS] testFullOracleGasCostObserveZero() (gas: 2090268349) +[PASS] testGas10SlotCardinalityGreater() (gas: 334542) +[PASS] testGas10Slots() (gas: 266320) +[PASS] testGas1Slot() (gas: 66176) +[PASS] testGas1SlotCardinalityGreater() (gas: 134429) +[PASS] testGasAllOfLast20Seconds() (gas: 262027) +[PASS] testGasBetweenOldestAndOldestPlusOne() (gas: 182500) +[PASS] testGasLatestEqual() (gas: 178357) +[PASS] testGasLatestTransform() (gas: 180008) +[PASS] testGasMiddle() (gas: 182642) +[PASS] testGasOldest() (gas: 182152) +[PASS] testGrow() (gas: 119740) +[PASS] testGrowAfterWrap() (gas: 73952) +[PASS] testInitialize() (gas: 89619) +[PASS] testInterpolatesAcrossChunkBoundaries() (gas: 110412) +[PASS] testInterpolatesSame0And1Liquidity() (gas: 108299) +[PASS] testInterpolationMaxLiquidity() (gas: 108286) +[PASS] testManyObservationsBetweenLatestObservationAfterLatest(uint32) (runs: 256, μ: 178510, ~: 178510) +[PASS] testManyObservationsBetweenLatestObservationAtLatest(uint32) (runs: 256, μ: 177475, ~: 177475) +[PASS] testManyObservationsCurrentObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174759, ~: 174759) +[PASS] testManyObservationsFetchManyValues(uint32) (runs: 256, μ: 202297, ~: 202292) +[PASS] testManyObservationsLatestObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174860, ~: 174860) +[PASS] testManyObservationsLatestObservationSameTimeAsLatest(uint32) (runs: 256, μ: 173207, ~: 173207) +[PASS] testManyObservationsOlderThanOldestReverts(uint32) (runs: 256, μ: 189853, ~: 189853) +[PASS] testManyObservationsOldest(uint32) (runs: 256, μ: 176962, ~: 176962) +[PASS] testManyObservationsOldestAfterTime(uint32) (runs: 256, μ: 178018, ~: 178018) +[PASS] testManyObservationsSimpleReads(uint32) (runs: 256, μ: 170946, ~: 170946) +[PASS] testObserveFailsBeforeInitialize() (gas: 14075) +[PASS] testObserveFailsIfOlderDoesNotExist() (gas: 61464) +[PASS] testObserveGasCurrentTime() (gas: 62111) +[PASS] testObserveGasCurrentTimeCounterfactual() (gas: 72518) +[PASS] testObserveGasSinceMostRecent() (gas: 64214) +[PASS] testSingleObservationAtCurrentTime() (gas: 56702) +[PASS] testSingleObservationInPastCounterfactualInPast() (gas: 58870) +[PASS] testSingleObservationInPastCounterfactualNow() (gas: 58212) +[PASS] testSingleObservationInRecentPast() (gas: 62566) +[PASS] testSingleObservationSecondsAgo() (gas: 58334) +[PASS] testTwoObservationsChronologicalSecondsAgoBetween() (gas: 88680) +[PASS] testTwoObservationsChronologicalSecondsAgoExactlyFirstObservation() (gas: 88383) +[PASS] testTwoObservationsChronologicalZeroSecondsAgoCounterfactual() (gas: 86168) +[PASS] testTwoObservationsChronologicalZeroSecondsAgoExact() (gas: 84658) +[PASS] testTwoObservationsReverseOrderSecondsAgoBetween() (gas: 93005) +[PASS] testTwoObservationsReverseOrderSecondsAgoExactlyOnFirstObservation() (gas: 92709) +[PASS] testTwoObservationsReverseOrderZeroSecondsAgoCounterfactual() (gas: 90537) +[PASS] testTwoObservationsReverseOrderZeroSecondsAgoExact() (gas: 88961) +[PASS] testWrite() (gas: 36417) +[PASS] testWriteAccumulatesLiquidity() (gas: 103839) +[PASS] testWriteAddsNothingIfTimeUnchanged() (gas: 44663) +[PASS] testWriteGrowsCardinalityWritingPast() (gas: 95433) +[PASS] testWriteTimeChanged() (gas: 70010) +[PASS] testWriteWrapsAround() (gas: 76186) +Test result: ok. 60 passed; 0 failed; 0 skipped; finished in 2.24s +| contracts/hooks/examples/FullRange.sol:FullRange contract | | | | | | +|-----------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 3489540 | 18155 | | | | | +| Function Name | min | avg | median | max | # calls | +| addLiquidity | 7858 | 312766 | 394894 | 394894 | 41 | +| beforeInitialize | 995 | 810205 | 828441 | 837441 | 38 | +| beforeModifyPosition | 893 | 947 | 949 | 949 | 49 | +| beforeSwap | 2202 | 2687 | 2495 | 5295 | 10 | +| lockAcquired | 8583 | 225213 | 269698 | 327800 | 45 | +| poolInfo | 594 | 816 | 594 | 2594 | 27 | +| removeLiquidity | 7978 | 142805 | 92468 | 353950 | 8 | + + +| contracts/hooks/examples/GeomeanOracle.sol:GeomeanOracle contract | | | | | | +|-------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 1817225 | 9744 | | | | | +| Function Name | min | avg | median | max | # calls | +| afterInitialize | 46668 | 46668 | 46668 | 46668 | 7 | +| beforeInitialize | 1057 | 1860 | 1966 | 1966 | 9 | +| beforeModifyPosition | 9365 | 10054 | 10380 | 10419 | 3 | +| getObservation | 1815 | 1815 | 1815 | 1815 | 5 | +| getState | 1618 | 1618 | 1618 | 1618 | 5 | +| increaseCardinalityNext | 24132 | 24132 | 24132 | 24132 | 1 | +| observe | 8822 | 8822 | 8822 | 8822 | 1 | + + +| contracts/hooks/examples/LimitOrder.sol:LimitOrder contract | | | | | | +|-------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 2680961 | 14064 | | | | | +| Function Name | min | avg | median | max | # calls | +| afterInitialize | 4946 | 6272 | 4946 | 24846 | 15 | +| afterSwap | 7048 | 62217 | 32587 | 147018 | 3 | +| epochInfos | 1292 | 2625 | 1292 | 5292 | 3 | +| epochNext | 2417 | 2417 | 2417 | 2417 | 1 | +| getEpoch | 1851 | 1851 | 1851 | 1851 | 4 | +| getEpochLiquidity | 821 | 821 | 821 | 821 | 2 | +| getTickLowerLast | 531 | 1197 | 531 | 2531 | 3 | +| kill | 72534 | 72534 | 72534 | 72534 | 1 | +| lockAcquired | 39381 | 156560 | 187504 | 216036 | 14 | +| lockAcquiredFill | 75428 | 75428 | 75428 | 75428 | 1 | +| lockAcquiredKill | 40377 | 40377 | 40377 | 40377 | 1 | +| lockAcquiredPlace | 38401 | 182736 | 188324 | 214811 | 11 | +| lockAcquiredWithdraw | 50123 | 50123 | 50123 | 50123 | 1 | +| onERC1155Received | 917 | 917 | 917 | 917 | 1 | +| place | 902 | 262930 | 288664 | 372312 | 12 | +| withdraw | 74418 | 74418 | 74418 | 74418 | 1 | + + +| contracts/hooks/examples/TWAMM.sol:TWAMM contract | | | | | | +|---------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 5497774 | 28258 | | | | | +| Function Name | min | avg | median | max | # calls | +| beforeInitialize | 22144 | 24082 | 24244 | 24244 | 13 | +| beforeModifyPosition | 5793 | 8011 | 5793 | 15893 | 37 | +| beforeSwap | 6304 | 6304 | 6304 | 6304 | 1 | +| claimTokens | 4491 | 4491 | 4491 | 4491 | 2 | +| executeTWAMMOrders | 89245 | 122431 | 122431 | 155618 | 2 | +| getOrder | 2672 | 3338 | 2672 | 6672 | 6 | +| getOrderPool | 1983 | 2188 | 1988 | 3993 | 10 | +| lastVirtualOrderTimestamp | 510 | 1510 | 1510 | 2510 | 2 | +| lockAcquired | 160064 | 160064 | 160064 | 160064 | 1 | +| submitOrder | 92000 | 129739 | 106972 | 626744 | 21 | +| tokensOwed | 731 | 731 | 731 | 731 | 16 | +| updateOrder | 9607 | 138511 | 183704 | 228998 | 11 | + + +| contracts/libraries/UniswapV4ERC20.sol:UniswapV4ERC20 contract | | | | | | +|----------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 764329 | 4835 | | | | | +| Function Name | min | avg | median | max | # calls | +| approve | 24546 | 24546 | 24546 | 24546 | 7 | +| balanceOf | 608 | 733 | 608 | 2608 | 16 | +| burn | 3047 | 6247 | 3047 | 12647 | 6 | +| mint | 2981 | 33152 | 24881 | 46781 | 70 | +| totalSupply | 363 | 1220 | 363 | 2363 | 7 | + + +| lib/v4-core/contracts/PoolManager.sol:PoolManager contract | | | | | | +|------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 5657368 | 28767 | | | | | +| Function Name | min | avg | median | max | # calls | +| MAX_TICK_SPACING | 277 | 277 | 277 | 277 | 11 | +| donate | 19952 | 19952 | 19952 | 19952 | 2 | +| extsload | 426 | 1854 | 2426 | 2426 | 14 | +| getLiquidity(bytes32)(uint128) | 593 | 1534 | 593 | 2593 | 136 | +| getLiquidity(bytes32,address,int24,int24)(uint128) | 1106 | 1106 | 1106 | 1106 | 5 | +| getSlot0 | 1007 | 1236 | 1007 | 3007 | 131 | +| initialize | 6002 | 453809 | 85853 | 873521 | 75 | +| lock | 34078 | 217471 | 217630 | 346022 | 112 | +| mint | 24253 | 24253 | 24253 | 24253 | 1 | +| modifyPosition | 3771 | 171722 | 191163 | 224594 | 102 | +| onERC1155Received | 21080 | 21080 | 21080 | 21080 | 1 | +| safeTransferFrom | 43814 | 43814 | 43814 | 43814 | 1 | +| settle | 3240 | 12630 | 20760 | 20760 | 173 | +| swap | 20223 | 92694 | 96576 | 232442 | 14 | +| take | 5049 | 6759 | 5049 | 18569 | 24 | + + +| lib/v4-core/contracts/test/PoolModifyPositionTest.sol:PoolModifyPositionTest contract | | | | | | +|---------------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 770392 | 4092 | | | | | +| Function Name | min | avg | median | max | # calls | +| lockAcquired | 6414 | 203177 | 172180 | 310314 | 41 | +| modifyPosition | 37560 | 227134 | 190692 | 340799 | 41 | + + +| lib/v4-core/contracts/test/PoolSwapTest.sol:PoolSwapTest contract | | | | | | +|-------------------------------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 952865 | 5045 | | | | | +| Function Name | min | avg | median | max | # calls | +| lockAcquired | 41524 | 106045 | 94376 | 290123 | 11 | +| swap | 75920 | 131508 | 118116 | 311623 | 11 | + + +| lib/v4-core/test/foundry-tests/utils/MockERC20.sol:MockERC20 contract | | | | | | +|-----------------------------------------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 701701 | 4609 | | | | | +| Function Name | min | avg | median | max | # calls | +| approve | 22446 | 24530 | 24546 | 24546 | 270 | +| balanceOf | 564 | 673 | 564 | 2564 | 219 | +| mint | 2862 | 6333 | 2862 | 29562 | 28 | +| symbol | 1191 | 2028 | 1191 | 3191 | 74 | +| transfer | 2362 | 5773 | 2952 | 29652 | 29 | +| transferFrom | 2948 | 17487 | 24308 | 32048 | 195 | + + +| test/shared/GetSender.sol:GetSender contract | | | | | | +|----------------------------------------------|-----------------|-----|--------|-----|---------| +| Deployment Cost | Deployment Size | | | | | +| 23675 | 147 | | | | | +| Function Name | min | avg | median | max | # calls | +| sender | 148 | 148 | 148 | 148 | 5 | + + +| test/shared/implementation/OracleImplementation.sol:OracleImplementation contract | | | | | | +|-----------------------------------------------------------------------------------|-----------------|-----------|--------|------------|---------| +| Deployment Cost | Deployment Size | | | | | +| 1185027 | 5951 | | | | | +| Function Name | min | avg | median | max | # calls | +| advanceTime | 591 | 775 | 591 | 5391 | 26 | +| batchUpdate | 711199 | 711199 | 711199 | 711258 | 1752 | +| cardinality | 377 | 377 | 377 | 377 | 9 | +| cardinalityNext | 399 | 399 | 399 | 399 | 14 | +| getGasCostOfObserve | 2383 | 12755 | 4804 | 76826 | 16 | +| grow | 793 | 224259672 | 89782 | 1457345922 | 52 | +| index | 356 | 356 | 356 | 356 | 18 | +| initialize | 46206 | 46206 | 46206 | 46206 | 107 | +| observations | 691 | 926 | 691 | 2691 | 17 | +| observe | 2673 | 7264 | 4604 | 26419 | 48 | +| time | 326 | 326 | 326 | 326 | 1 | +| update | 2650 | 3890 | 3678 | 13288 | 130 | + + +| test/utils/HookMiner.sol:HookMiner contract | | | | | | +|---------------------------------------------|-----------------|--------|--------|---------|---------| +| Deployment Cost | Deployment Size | | | | | +| 238306 | 1248 | | | | | +| Function Name | min | avg | median | max | # calls | +| find | 169330 | 673493 | 696888 | 1052336 | 56 | + + + + +Ran 5 test suites: 116 tests passed, 0 failed, 0 skipped (116 total tests) From 2ad898869e60d6807a109e49417f935c3d1c10c2 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:40:06 -0400 Subject: [PATCH 12/17] refine hookminer for minor gas improvements --- test/GeomeanOracle.t.sol | 9 +++++---- test/utils/HookMiner.sol | 23 +++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/GeomeanOracle.t.sol b/test/GeomeanOracle.t.sol index 1e45f98e..15a530bc 100644 --- a/test/GeomeanOracle.t.sol +++ b/test/GeomeanOracle.t.sol @@ -41,10 +41,11 @@ contract TestGeomeanOracle is Test, Deployers, TokenFixture { manager = new PoolManager(500000); uint160 flags = uint160( - Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG - | Hooks.BEFORE_SWAP_FLAG - ); - (address hookAddress, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(GeomeanOracle).creationCode, abi.encode(manager)); + Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG + | Hooks.BEFORE_SWAP_FLAG + ); + (address hookAddress, bytes32 salt) = + HookMiner.find(address(this), flags, 0, type(GeomeanOracle).creationCode, abi.encode(manager)); geomeanOracle = new GeomeanOracle{salt: salt}(manager); require(address(geomeanOracle) == hookAddress, "TestGeomeanOracle: hook address mismatch"); diff --git a/test/utils/HookMiner.sol b/test/utils/HookMiner.sol index 5b55e982..21852c8a 100644 --- a/test/utils/HookMiner.sol +++ b/test/utils/HookMiner.sol @@ -17,30 +17,29 @@ library HookMiner { /// @param seed Use 0 for as a default. An optional starting salt when linearly searching for a salt. Useful for finding salts for multiple hooks with the same flags /// @param creationCode The creation code of a hook contract. Example: `type(Counter).creationCode` /// @param constructorArgs The encoded constructor arguments of a hook contract. Example: `abi.encode(address(manager))` - /// @return hookAddress the salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}()` + /// @return hookAddress salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}()` function find( address deployer, uint160 flags, uint256 seed, bytes memory creationCode, bytes memory constructorArgs - ) external pure returns (address hookAddress, bytes32 salt) { + ) external pure returns (address, bytes32) { + address hookAddress; bytes memory creationCodeWithArgs = abi.encodePacked(creationCode, constructorArgs); - uint160 prefix; - uint256 i = seed; - for (i; i < MAX_LOOP;) { - hookAddress = computeAddress(deployer, i, creationCodeWithArgs); - prefix = uint160(hookAddress) & FLAG_MASK; - if (prefix == flags) { - break; + + uint256 salt = seed; + for (salt; salt < MAX_LOOP;) { + hookAddress = computeAddress(deployer, salt, creationCodeWithArgs); + if (uint160(hookAddress) & FLAG_MASK == flags) { + return (hookAddress, bytes32(salt)); } unchecked { - ++i; + ++salt; } } - salt = bytes32(i); - require(uint160(hookAddress) & FLAG_MASK == flags, "HookMiner: could not find hook address"); + revert("HookMiner: could not find salt"); } /// @notice Precompute a contract address deployed via CREATE2 From 49bf45afad5a0be66152fd1cac6f45382610dfec Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Sep 2023 15:40:31 -0400 Subject: [PATCH 13/17] remove gas reports --- .gas-report | 307 -------------------------------------------------- .gas-snapshot | 116 ------------------- 2 files changed, 423 deletions(-) delete mode 100644 .gas-report delete mode 100644 .gas-snapshot diff --git a/.gas-report b/.gas-report deleted file mode 100644 index f01561b4..00000000 --- a/.gas-report +++ /dev/null @@ -1,307 +0,0 @@ -Compiling 5 files with 0.8.20 -Solc 0.8.20 finished in 6.68s -Compiler run successful! - -Running 9 tests for test/GeomeanOracle.t.sol:TestGeomeanOracle -[PASS] testAfterInitializeObservation() (gas: 103263) -[PASS] testAfterInitializeObserve0() (gas: 111551) -[PASS] testAfterInitializeState() (gas: 102994) -[PASS] testBeforeInitializeAllowsPoolCreation() (gas: 97765) -[PASS] testBeforeInitializeRevertsIfFee() (gas: 21174) -[PASS] testBeforeInitializeRevertsIfNotMaxTickSpacing() (gas: 22006) -[PASS] testBeforeModifyPositionNoObservations() (gas: 451900) -[PASS] testBeforeModifyPositionObservation() (gas: 455898) -[PASS] testBeforeModifyPositionObservationAndCardinality() (gas: 487676) -Test result: ok. 9 passed; 0 failed; 0 skipped; finished in 18.47ms - -Running 12 tests for test/TWAMM.t.sol:TWAMMTest -[PASS] testTWAMMEndToEndSimSymmetricalOrderPools() (gas: 796056) -[PASS] testTWAMM_beforeInitialize_SetsLastVirtualOrderTimestamp() (gas: 1543971) -[PASS] testTWAMM_submitOrder_EmitsEvent() (gas: 176414) -[PASS] testTWAMM_submitOrder_StoresOrderWithCorrectPoolAndOrderPoolInfo() (gas: 218226) -[PASS] testTWAMM_submitOrder_StoresSellRatesEarningsFactorsProperly() (gas: 955160) -[PASS] testTWAMM_updateOrder_EmitsEvent() (gas: 436339) -[PASS] testTWAMM_updateOrder_OneForZero_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514301) -[PASS] testTWAMM_updateOrder_ZeroForOne_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514231) -[PASS] testTWAMM_updatedOrder_OneForZero_ClosesOrderIfEliminatingPosition() (gas: 437734) -[PASS] testTWAMM_updatedOrder_OneForZero_IncreaseOrderAmount() (gas: 502469) -[PASS] testTWAMM_updatedOrder_ZeroForOne_ClosesOrderIfEliminatingPosition() (gas: 437742) -[PASS] testTWAMM_updatedOrder_ZeroForOne_IncreaseOrderAmount() (gas: 502467) -Test result: ok. 12 passed; 0 failed; 0 skipped; finished in 67.81ms - -Running 21 tests for test/FullRange.t.sol:TestFullRange -[PASS] testFullRange_BeforeModifyPositionFailsWithWrongMsgSender() (gas: 931705) -[PASS] testFullRange_addLiquidity_FailsIfNoPool() (gas: 20712) -[PASS] testFullRange_addLiquidity_FailsIfTooMuchSlippage() (gas: 1417713) -[PASS] testFullRange_addLiquidity_InitialAddFuzz(uint256) (runs: 256, μ: 1067175, ~: 901919) -[PASS] testFullRange_addLiquidity_InitialAddSucceeds() (gas: 1287095) -[PASS] testFullRange_addLiquidity_SubsequentAdd() (gas: 235736) -[PASS] testFullRange_addLiquidity_SwapThenAddSucceeds() (gas: 1471474) -[PASS] testFullRange_beforeInitialize_AllowsPoolCreation() (gas: 927663) -[PASS] testFullRange_beforeInitialize_RevertsIfWrongSpacing() (gas: 21207) -[PASS] testFullRange_removeLiquidity_DiffRatios() (gas: 1405163) -[PASS] testFullRange_removeLiquidity_FailsIfNoLiquidity() (gas: 959576) -[PASS] testFullRange_removeLiquidity_FailsIfNoPool() (gas: 20523) -[PASS] testFullRange_removeLiquidity_InitialRemoveFuzz(uint256) (runs: 256, μ: 1319139, ~: 1334491) -[PASS] testFullRange_removeLiquidity_InitialRemoveSucceeds() (gas: 248863) -[PASS] testFullRange_removeLiquidity_RemoveAllFuzz(uint256) (runs: 256, μ: 1082176, ~: 905318) -[PASS] testFullRange_removeLiquidity_SucceedsWithPartial() (gas: 1341038) -[PASS] testFullRange_removeLiquidity_SwapAndRebalance() (gas: 579224) -[PASS] testFullRange_removeLiquidity_SwapRemoveAllFuzz(uint256) (runs: 256, μ: 1190490, ~: 905264) -[PASS] testFullRange_removeLiquidity_ThreeLPsRemovePrincipalAndFees() (gas: 1917405) -[PASS] testFullRange_swap_TwoPools() (gas: 2596836) -[PASS] testFullRange_swap_TwoSwaps() (gas: 1430392) -Test result: ok. 21 passed; 0 failed; 0 skipped; finished in 1.39s - -Running 14 tests for test/LimitOrder.t.sol:TestLimitOrder -[PASS] testEpochNext() (gas: 7744) -[PASS] testGetTickLowerLast() (gas: 9959) -[PASS] testGetTickLowerLastWithDifferentPrice() (gas: 74899) -[PASS] testKill() (gas: 505672) -[PASS] testMultipleLPs() (gas: 611748) -[PASS] testNotZeroForOneCrossedRangeRevert() (gas: 239469) -[PASS] testNotZeroForOneInRangeRevert() (gas: 359048) -[PASS] testNotZeroForOneLeftBoundaryOfCurrentRange() (gas: 391999) -[PASS] testSwapAcrossRange() (gas: 833945) -[PASS] testZeroForOneCrossedRangeRevert() (gas: 238259) -[PASS] testZeroForOneInRangeRevert() (gas: 306791) -[PASS] testZeroForOneLeftBoundaryOfCurrentRange() (gas: 393271) -[PASS] testZeroForOneRightBoundaryOfCurrentRange() (gas: 364741) -[PASS] testZeroLiquidityRevert() (gas: 15562) -Test result: ok. 14 passed; 0 failed; 0 skipped; finished in 2.24s - -Running 60 tests for test/Oracle.t.sol:TestOracle -[PASS] testCanFetchMultipleObservations() (gas: 154980) -[PASS] testDoesNotFailAcrossOverflowBoundary() (gas: 58950) -[PASS] testFullOracle() (gas: 2090424997) -[PASS] testFullOracleGasCostObserve0After5Seconds() (gas: 2090269961) -[PASS] testFullOracleGasCostObserve200By13() (gas: 2090287915) -[PASS] testFullOracleGasCostObserve200By13Plus5() (gas: 2090288121) -[PASS] testFullOracleGasCostObserve5After5Seconds() (gas: 2090270060) -[PASS] testFullOracleGasCostObserveOldest() (gas: 2090287010) -[PASS] testFullOracleGasCostObserveOldestAfter5Seconds() (gas: 2090288483) -[PASS] testFullOracleGasCostObserveZero() (gas: 2090268349) -[PASS] testGas10SlotCardinalityGreater() (gas: 334542) -[PASS] testGas10Slots() (gas: 266320) -[PASS] testGas1Slot() (gas: 66176) -[PASS] testGas1SlotCardinalityGreater() (gas: 134429) -[PASS] testGasAllOfLast20Seconds() (gas: 262027) -[PASS] testGasBetweenOldestAndOldestPlusOne() (gas: 182500) -[PASS] testGasLatestEqual() (gas: 178357) -[PASS] testGasLatestTransform() (gas: 180008) -[PASS] testGasMiddle() (gas: 182642) -[PASS] testGasOldest() (gas: 182152) -[PASS] testGrow() (gas: 119740) -[PASS] testGrowAfterWrap() (gas: 73952) -[PASS] testInitialize() (gas: 89619) -[PASS] testInterpolatesAcrossChunkBoundaries() (gas: 110412) -[PASS] testInterpolatesSame0And1Liquidity() (gas: 108299) -[PASS] testInterpolationMaxLiquidity() (gas: 108286) -[PASS] testManyObservationsBetweenLatestObservationAfterLatest(uint32) (runs: 256, μ: 178510, ~: 178510) -[PASS] testManyObservationsBetweenLatestObservationAtLatest(uint32) (runs: 256, μ: 177475, ~: 177475) -[PASS] testManyObservationsCurrentObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174759, ~: 174759) -[PASS] testManyObservationsFetchManyValues(uint32) (runs: 256, μ: 202297, ~: 202292) -[PASS] testManyObservationsLatestObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174860, ~: 174860) -[PASS] testManyObservationsLatestObservationSameTimeAsLatest(uint32) (runs: 256, μ: 173207, ~: 173207) -[PASS] testManyObservationsOlderThanOldestReverts(uint32) (runs: 256, μ: 189853, ~: 189853) -[PASS] testManyObservationsOldest(uint32) (runs: 256, μ: 176962, ~: 176962) -[PASS] testManyObservationsOldestAfterTime(uint32) (runs: 256, μ: 178018, ~: 178018) -[PASS] testManyObservationsSimpleReads(uint32) (runs: 256, μ: 170946, ~: 170946) -[PASS] testObserveFailsBeforeInitialize() (gas: 14075) -[PASS] testObserveFailsIfOlderDoesNotExist() (gas: 61464) -[PASS] testObserveGasCurrentTime() (gas: 62111) -[PASS] testObserveGasCurrentTimeCounterfactual() (gas: 72518) -[PASS] testObserveGasSinceMostRecent() (gas: 64214) -[PASS] testSingleObservationAtCurrentTime() (gas: 56702) -[PASS] testSingleObservationInPastCounterfactualInPast() (gas: 58870) -[PASS] testSingleObservationInPastCounterfactualNow() (gas: 58212) -[PASS] testSingleObservationInRecentPast() (gas: 62566) -[PASS] testSingleObservationSecondsAgo() (gas: 58334) -[PASS] testTwoObservationsChronologicalSecondsAgoBetween() (gas: 88680) -[PASS] testTwoObservationsChronologicalSecondsAgoExactlyFirstObservation() (gas: 88383) -[PASS] testTwoObservationsChronologicalZeroSecondsAgoCounterfactual() (gas: 86168) -[PASS] testTwoObservationsChronologicalZeroSecondsAgoExact() (gas: 84658) -[PASS] testTwoObservationsReverseOrderSecondsAgoBetween() (gas: 93005) -[PASS] testTwoObservationsReverseOrderSecondsAgoExactlyOnFirstObservation() (gas: 92709) -[PASS] testTwoObservationsReverseOrderZeroSecondsAgoCounterfactual() (gas: 90537) -[PASS] testTwoObservationsReverseOrderZeroSecondsAgoExact() (gas: 88961) -[PASS] testWrite() (gas: 36417) -[PASS] testWriteAccumulatesLiquidity() (gas: 103839) -[PASS] testWriteAddsNothingIfTimeUnchanged() (gas: 44663) -[PASS] testWriteGrowsCardinalityWritingPast() (gas: 95433) -[PASS] testWriteTimeChanged() (gas: 70010) -[PASS] testWriteWrapsAround() (gas: 76186) -Test result: ok. 60 passed; 0 failed; 0 skipped; finished in 2.24s -| contracts/hooks/examples/FullRange.sol:FullRange contract | | | | | | -|-----------------------------------------------------------|-----------------|--------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 3489540 | 18155 | | | | | -| Function Name | min | avg | median | max | # calls | -| addLiquidity | 7858 | 312766 | 394894 | 394894 | 41 | -| beforeInitialize | 995 | 810205 | 828441 | 837441 | 38 | -| beforeModifyPosition | 893 | 947 | 949 | 949 | 49 | -| beforeSwap | 2202 | 2687 | 2495 | 5295 | 10 | -| lockAcquired | 8583 | 225213 | 269698 | 327800 | 45 | -| poolInfo | 594 | 816 | 594 | 2594 | 27 | -| removeLiquidity | 7978 | 142805 | 92468 | 353950 | 8 | - - -| contracts/hooks/examples/GeomeanOracle.sol:GeomeanOracle contract | | | | | | -|-------------------------------------------------------------------|-----------------|-------|--------|-------|---------| -| Deployment Cost | Deployment Size | | | | | -| 1817225 | 9744 | | | | | -| Function Name | min | avg | median | max | # calls | -| afterInitialize | 46668 | 46668 | 46668 | 46668 | 7 | -| beforeInitialize | 1057 | 1860 | 1966 | 1966 | 9 | -| beforeModifyPosition | 9365 | 10054 | 10380 | 10419 | 3 | -| getObservation | 1815 | 1815 | 1815 | 1815 | 5 | -| getState | 1618 | 1618 | 1618 | 1618 | 5 | -| increaseCardinalityNext | 24132 | 24132 | 24132 | 24132 | 1 | -| observe | 8822 | 8822 | 8822 | 8822 | 1 | - - -| contracts/hooks/examples/LimitOrder.sol:LimitOrder contract | | | | | | -|-------------------------------------------------------------|-----------------|--------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 2680961 | 14064 | | | | | -| Function Name | min | avg | median | max | # calls | -| afterInitialize | 4946 | 6272 | 4946 | 24846 | 15 | -| afterSwap | 7048 | 62217 | 32587 | 147018 | 3 | -| epochInfos | 1292 | 2625 | 1292 | 5292 | 3 | -| epochNext | 2417 | 2417 | 2417 | 2417 | 1 | -| getEpoch | 1851 | 1851 | 1851 | 1851 | 4 | -| getEpochLiquidity | 821 | 821 | 821 | 821 | 2 | -| getTickLowerLast | 531 | 1197 | 531 | 2531 | 3 | -| kill | 72534 | 72534 | 72534 | 72534 | 1 | -| lockAcquired | 39381 | 156560 | 187504 | 216036 | 14 | -| lockAcquiredFill | 75428 | 75428 | 75428 | 75428 | 1 | -| lockAcquiredKill | 40377 | 40377 | 40377 | 40377 | 1 | -| lockAcquiredPlace | 38401 | 182736 | 188324 | 214811 | 11 | -| lockAcquiredWithdraw | 50123 | 50123 | 50123 | 50123 | 1 | -| onERC1155Received | 917 | 917 | 917 | 917 | 1 | -| place | 902 | 262930 | 288664 | 372312 | 12 | -| withdraw | 74418 | 74418 | 74418 | 74418 | 1 | - - -| contracts/hooks/examples/TWAMM.sol:TWAMM contract | | | | | | -|---------------------------------------------------|-----------------|--------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 5497774 | 28258 | | | | | -| Function Name | min | avg | median | max | # calls | -| beforeInitialize | 22144 | 24082 | 24244 | 24244 | 13 | -| beforeModifyPosition | 5793 | 8011 | 5793 | 15893 | 37 | -| beforeSwap | 6304 | 6304 | 6304 | 6304 | 1 | -| claimTokens | 4491 | 4491 | 4491 | 4491 | 2 | -| executeTWAMMOrders | 89245 | 122431 | 122431 | 155618 | 2 | -| getOrder | 2672 | 3338 | 2672 | 6672 | 6 | -| getOrderPool | 1983 | 2188 | 1988 | 3993 | 10 | -| lastVirtualOrderTimestamp | 510 | 1510 | 1510 | 2510 | 2 | -| lockAcquired | 160064 | 160064 | 160064 | 160064 | 1 | -| submitOrder | 92000 | 129739 | 106972 | 626744 | 21 | -| tokensOwed | 731 | 731 | 731 | 731 | 16 | -| updateOrder | 9607 | 138511 | 183704 | 228998 | 11 | - - -| contracts/libraries/UniswapV4ERC20.sol:UniswapV4ERC20 contract | | | | | | -|----------------------------------------------------------------|-----------------|-------|--------|-------|---------| -| Deployment Cost | Deployment Size | | | | | -| 764329 | 4835 | | | | | -| Function Name | min | avg | median | max | # calls | -| approve | 24546 | 24546 | 24546 | 24546 | 7 | -| balanceOf | 608 | 733 | 608 | 2608 | 16 | -| burn | 3047 | 6247 | 3047 | 12647 | 6 | -| mint | 2981 | 33152 | 24881 | 46781 | 70 | -| totalSupply | 363 | 1220 | 363 | 2363 | 7 | - - -| lib/v4-core/contracts/PoolManager.sol:PoolManager contract | | | | | | -|------------------------------------------------------------|-----------------|--------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 5657368 | 28767 | | | | | -| Function Name | min | avg | median | max | # calls | -| MAX_TICK_SPACING | 277 | 277 | 277 | 277 | 11 | -| donate | 19952 | 19952 | 19952 | 19952 | 2 | -| extsload | 426 | 1854 | 2426 | 2426 | 14 | -| getLiquidity(bytes32)(uint128) | 593 | 1534 | 593 | 2593 | 136 | -| getLiquidity(bytes32,address,int24,int24)(uint128) | 1106 | 1106 | 1106 | 1106 | 5 | -| getSlot0 | 1007 | 1236 | 1007 | 3007 | 131 | -| initialize | 6002 | 453809 | 85853 | 873521 | 75 | -| lock | 34078 | 217471 | 217630 | 346022 | 112 | -| mint | 24253 | 24253 | 24253 | 24253 | 1 | -| modifyPosition | 3771 | 171722 | 191163 | 224594 | 102 | -| onERC1155Received | 21080 | 21080 | 21080 | 21080 | 1 | -| safeTransferFrom | 43814 | 43814 | 43814 | 43814 | 1 | -| settle | 3240 | 12630 | 20760 | 20760 | 173 | -| swap | 20223 | 92694 | 96576 | 232442 | 14 | -| take | 5049 | 6759 | 5049 | 18569 | 24 | - - -| lib/v4-core/contracts/test/PoolModifyPositionTest.sol:PoolModifyPositionTest contract | | | | | | -|---------------------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 770392 | 4092 | | | | | -| Function Name | min | avg | median | max | # calls | -| lockAcquired | 6414 | 203177 | 172180 | 310314 | 41 | -| modifyPosition | 37560 | 227134 | 190692 | 340799 | 41 | - - -| lib/v4-core/contracts/test/PoolSwapTest.sol:PoolSwapTest contract | | | | | | -|-------------------------------------------------------------------|-----------------|--------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 952865 | 5045 | | | | | -| Function Name | min | avg | median | max | # calls | -| lockAcquired | 41524 | 106045 | 94376 | 290123 | 11 | -| swap | 75920 | 131508 | 118116 | 311623 | 11 | - - -| lib/v4-core/test/foundry-tests/utils/MockERC20.sol:MockERC20 contract | | | | | | -|-----------------------------------------------------------------------|-----------------|-------|--------|-------|---------| -| Deployment Cost | Deployment Size | | | | | -| 701701 | 4609 | | | | | -| Function Name | min | avg | median | max | # calls | -| approve | 22446 | 24530 | 24546 | 24546 | 270 | -| balanceOf | 564 | 673 | 564 | 2564 | 219 | -| mint | 2862 | 6333 | 2862 | 29562 | 28 | -| symbol | 1191 | 2028 | 1191 | 3191 | 74 | -| transfer | 2362 | 5773 | 2952 | 29652 | 29 | -| transferFrom | 2948 | 17487 | 24308 | 32048 | 195 | - - -| test/shared/GetSender.sol:GetSender contract | | | | | | -|----------------------------------------------|-----------------|-----|--------|-----|---------| -| Deployment Cost | Deployment Size | | | | | -| 23675 | 147 | | | | | -| Function Name | min | avg | median | max | # calls | -| sender | 148 | 148 | 148 | 148 | 5 | - - -| test/shared/implementation/OracleImplementation.sol:OracleImplementation contract | | | | | | -|-----------------------------------------------------------------------------------|-----------------|-----------|--------|------------|---------| -| Deployment Cost | Deployment Size | | | | | -| 1185027 | 5951 | | | | | -| Function Name | min | avg | median | max | # calls | -| advanceTime | 591 | 775 | 591 | 5391 | 26 | -| batchUpdate | 711199 | 711199 | 711199 | 711258 | 1752 | -| cardinality | 377 | 377 | 377 | 377 | 9 | -| cardinalityNext | 399 | 399 | 399 | 399 | 14 | -| getGasCostOfObserve | 2383 | 12755 | 4804 | 76826 | 16 | -| grow | 793 | 224259672 | 89782 | 1457345922 | 52 | -| index | 356 | 356 | 356 | 356 | 18 | -| initialize | 46206 | 46206 | 46206 | 46206 | 107 | -| observations | 691 | 926 | 691 | 2691 | 17 | -| observe | 2673 | 7264 | 4604 | 26419 | 48 | -| time | 326 | 326 | 326 | 326 | 1 | -| update | 2650 | 3890 | 3678 | 13288 | 130 | - - -| test/utils/HookMiner.sol:HookMiner contract | | | | | | -|---------------------------------------------|-----------------|--------|--------|---------|---------| -| Deployment Cost | Deployment Size | | | | | -| 238306 | 1248 | | | | | -| Function Name | min | avg | median | max | # calls | -| find | 169330 | 673493 | 696888 | 1052336 | 56 | - - - - -Ran 5 test suites: 116 tests passed, 0 failed, 0 skipped (116 total tests) diff --git a/.gas-snapshot b/.gas-snapshot deleted file mode 100644 index 3671cd93..00000000 --- a/.gas-snapshot +++ /dev/null @@ -1,116 +0,0 @@ -TWAMMTest:testTWAMMEndToEndSimSymmetricalOrderPools() (gas: 796056) -TWAMMTest:testTWAMM_beforeInitialize_SetsLastVirtualOrderTimestamp() (gas: 1543971) -TWAMMTest:testTWAMM_submitOrder_EmitsEvent() (gas: 176414) -TWAMMTest:testTWAMM_submitOrder_StoresOrderWithCorrectPoolAndOrderPoolInfo() (gas: 218226) -TWAMMTest:testTWAMM_submitOrder_StoresSellRatesEarningsFactorsProperly() (gas: 955160) -TWAMMTest:testTWAMM_updateOrder_EmitsEvent() (gas: 436339) -TWAMMTest:testTWAMM_updateOrder_OneForZero_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514301) -TWAMMTest:testTWAMM_updateOrder_ZeroForOne_DecreasesSellrateUpdatesSellTokensOwed() (gas: 514231) -TWAMMTest:testTWAMM_updatedOrder_OneForZero_ClosesOrderIfEliminatingPosition() (gas: 437734) -TWAMMTest:testTWAMM_updatedOrder_OneForZero_IncreaseOrderAmount() (gas: 502469) -TWAMMTest:testTWAMM_updatedOrder_ZeroForOne_ClosesOrderIfEliminatingPosition() (gas: 437742) -TWAMMTest:testTWAMM_updatedOrder_ZeroForOne_IncreaseOrderAmount() (gas: 502467) -TestFullRange:testFullRange_BeforeModifyPositionFailsWithWrongMsgSender() (gas: 931705) -TestFullRange:testFullRange_addLiquidity_FailsIfNoPool() (gas: 20712) -TestFullRange:testFullRange_addLiquidity_FailsIfTooMuchSlippage() (gas: 1417713) -TestFullRange:testFullRange_addLiquidity_InitialAddFuzz(uint256) (runs: 256, μ: 1040150, ~: 901919) -TestFullRange:testFullRange_addLiquidity_InitialAddSucceeds() (gas: 1287095) -TestFullRange:testFullRange_addLiquidity_SubsequentAdd() (gas: 235736) -TestFullRange:testFullRange_addLiquidity_SwapThenAddSucceeds() (gas: 1471474) -TestFullRange:testFullRange_beforeInitialize_AllowsPoolCreation() (gas: 927663) -TestFullRange:testFullRange_beforeInitialize_RevertsIfWrongSpacing() (gas: 21207) -TestFullRange:testFullRange_removeLiquidity_DiffRatios() (gas: 1405163) -TestFullRange:testFullRange_removeLiquidity_FailsIfNoLiquidity() (gas: 959576) -TestFullRange:testFullRange_removeLiquidity_FailsIfNoPool() (gas: 20523) -TestFullRange:testFullRange_removeLiquidity_InitialRemoveFuzz(uint256) (runs: 256, μ: 1318073, ~: 1328396) -TestFullRange:testFullRange_removeLiquidity_InitialRemoveSucceeds() (gas: 248863) -TestFullRange:testFullRange_removeLiquidity_RemoveAllFuzz(uint256) (runs: 256, μ: 1072322, ~: 905318) -TestFullRange:testFullRange_removeLiquidity_SucceedsWithPartial() (gas: 1341038) -TestFullRange:testFullRange_removeLiquidity_SwapAndRebalance() (gas: 579224) -TestFullRange:testFullRange_removeLiquidity_SwapRemoveAllFuzz(uint256) (runs: 256, μ: 1157950, ~: 905264) -TestFullRange:testFullRange_removeLiquidity_ThreeLPsRemovePrincipalAndFees() (gas: 1917405) -TestFullRange:testFullRange_swap_TwoPools() (gas: 2596836) -TestFullRange:testFullRange_swap_TwoSwaps() (gas: 1430392) -TestGeomeanOracle:testAfterInitializeObservation() (gas: 103263) -TestGeomeanOracle:testAfterInitializeObserve0() (gas: 111551) -TestGeomeanOracle:testAfterInitializeState() (gas: 102994) -TestGeomeanOracle:testBeforeInitializeAllowsPoolCreation() (gas: 97765) -TestGeomeanOracle:testBeforeInitializeRevertsIfFee() (gas: 21174) -TestGeomeanOracle:testBeforeInitializeRevertsIfNotMaxTickSpacing() (gas: 22006) -TestGeomeanOracle:testBeforeModifyPositionNoObservations() (gas: 451900) -TestGeomeanOracle:testBeforeModifyPositionObservation() (gas: 455898) -TestGeomeanOracle:testBeforeModifyPositionObservationAndCardinality() (gas: 487676) -TestLimitOrder:testEpochNext() (gas: 7744) -TestLimitOrder:testGetTickLowerLast() (gas: 9959) -TestLimitOrder:testGetTickLowerLastWithDifferentPrice() (gas: 74899) -TestLimitOrder:testKill() (gas: 505672) -TestLimitOrder:testMultipleLPs() (gas: 611748) -TestLimitOrder:testNotZeroForOneCrossedRangeRevert() (gas: 239469) -TestLimitOrder:testNotZeroForOneInRangeRevert() (gas: 359048) -TestLimitOrder:testNotZeroForOneLeftBoundaryOfCurrentRange() (gas: 391999) -TestLimitOrder:testSwapAcrossRange() (gas: 833945) -TestLimitOrder:testZeroForOneCrossedRangeRevert() (gas: 238259) -TestLimitOrder:testZeroForOneInRangeRevert() (gas: 306791) -TestLimitOrder:testZeroForOneLeftBoundaryOfCurrentRange() (gas: 393271) -TestLimitOrder:testZeroForOneRightBoundaryOfCurrentRange() (gas: 364741) -TestLimitOrder:testZeroLiquidityRevert() (gas: 15562) -TestOracle:testCanFetchMultipleObservations() (gas: 154980) -TestOracle:testDoesNotFailAcrossOverflowBoundary() (gas: 58950) -TestOracle:testFullOracle() (gas: 2090424997) -TestOracle:testFullOracleGasCostObserve0After5Seconds() (gas: 2090269961) -TestOracle:testFullOracleGasCostObserve200By13() (gas: 2090287915) -TestOracle:testFullOracleGasCostObserve200By13Plus5() (gas: 2090288121) -TestOracle:testFullOracleGasCostObserve5After5Seconds() (gas: 2090270060) -TestOracle:testFullOracleGasCostObserveOldest() (gas: 2090287010) -TestOracle:testFullOracleGasCostObserveOldestAfter5Seconds() (gas: 2090288483) -TestOracle:testFullOracleGasCostObserveZero() (gas: 2090268349) -TestOracle:testGas10SlotCardinalityGreater() (gas: 334542) -TestOracle:testGas10Slots() (gas: 266320) -TestOracle:testGas1Slot() (gas: 66176) -TestOracle:testGas1SlotCardinalityGreater() (gas: 134429) -TestOracle:testGasAllOfLast20Seconds() (gas: 262027) -TestOracle:testGasBetweenOldestAndOldestPlusOne() (gas: 182500) -TestOracle:testGasLatestEqual() (gas: 178357) -TestOracle:testGasLatestTransform() (gas: 180008) -TestOracle:testGasMiddle() (gas: 182642) -TestOracle:testGasOldest() (gas: 182152) -TestOracle:testGrow() (gas: 119740) -TestOracle:testGrowAfterWrap() (gas: 73952) -TestOracle:testInitialize() (gas: 89619) -TestOracle:testInterpolatesAcrossChunkBoundaries() (gas: 110412) -TestOracle:testInterpolatesSame0And1Liquidity() (gas: 108299) -TestOracle:testInterpolationMaxLiquidity() (gas: 108286) -TestOracle:testManyObservationsBetweenLatestObservationAfterLatest(uint32) (runs: 256, μ: 178510, ~: 178510) -TestOracle:testManyObservationsBetweenLatestObservationAtLatest(uint32) (runs: 256, μ: 177475, ~: 177475) -TestOracle:testManyObservationsCurrentObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174759, ~: 174759) -TestOracle:testManyObservationsFetchManyValues(uint32) (runs: 256, μ: 202292, ~: 202292) -TestOracle:testManyObservationsLatestObservation5SecondsAfterLatest(uint32) (runs: 256, μ: 174860, ~: 174860) -TestOracle:testManyObservationsLatestObservationSameTimeAsLatest(uint32) (runs: 256, μ: 173207, ~: 173207) -TestOracle:testManyObservationsOlderThanOldestReverts(uint32) (runs: 256, μ: 189853, ~: 189853) -TestOracle:testManyObservationsOldest(uint32) (runs: 256, μ: 176962, ~: 176962) -TestOracle:testManyObservationsOldestAfterTime(uint32) (runs: 256, μ: 178018, ~: 178018) -TestOracle:testManyObservationsSimpleReads(uint32) (runs: 256, μ: 170946, ~: 170946) -TestOracle:testObserveFailsBeforeInitialize() (gas: 14075) -TestOracle:testObserveFailsIfOlderDoesNotExist() (gas: 61464) -TestOracle:testObserveGasCurrentTime() (gas: 62111) -TestOracle:testObserveGasCurrentTimeCounterfactual() (gas: 72518) -TestOracle:testObserveGasSinceMostRecent() (gas: 64214) -TestOracle:testSingleObservationAtCurrentTime() (gas: 56702) -TestOracle:testSingleObservationInPastCounterfactualInPast() (gas: 58870) -TestOracle:testSingleObservationInPastCounterfactualNow() (gas: 58212) -TestOracle:testSingleObservationInRecentPast() (gas: 62566) -TestOracle:testSingleObservationSecondsAgo() (gas: 58334) -TestOracle:testTwoObservationsChronologicalSecondsAgoBetween() (gas: 88680) -TestOracle:testTwoObservationsChronologicalSecondsAgoExactlyFirstObservation() (gas: 88383) -TestOracle:testTwoObservationsChronologicalZeroSecondsAgoCounterfactual() (gas: 86168) -TestOracle:testTwoObservationsChronologicalZeroSecondsAgoExact() (gas: 84658) -TestOracle:testTwoObservationsReverseOrderSecondsAgoBetween() (gas: 93005) -TestOracle:testTwoObservationsReverseOrderSecondsAgoExactlyOnFirstObservation() (gas: 92709) -TestOracle:testTwoObservationsReverseOrderZeroSecondsAgoCounterfactual() (gas: 90537) -TestOracle:testTwoObservationsReverseOrderZeroSecondsAgoExact() (gas: 88961) -TestOracle:testWrite() (gas: 36417) -TestOracle:testWriteAccumulatesLiquidity() (gas: 103839) -TestOracle:testWriteAddsNothingIfTimeUnchanged() (gas: 44663) -TestOracle:testWriteGrowsCardinalityWritingPast() (gas: 95433) -TestOracle:testWriteTimeChanged() (gas: 70010) -TestOracle:testWriteWrapsAround() (gas: 76186) \ No newline at end of file From 8ecaec746f5b8df8672a7222432c153992fe8147 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Mon, 25 Sep 2023 21:02:51 -0400 Subject: [PATCH 14/17] natspec cleanup --- test/utils/HookMiner.sol | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/utils/HookMiner.sol b/test/utils/HookMiner.sol index 21852c8a..f57dbf01 100644 --- a/test/utils/HookMiner.sol +++ b/test/utils/HookMiner.sol @@ -11,13 +11,16 @@ library HookMiner { uint256 constant MAX_LOOP = 10_000; /// @notice Find a salt that produces a hook address with the desired `flags` - /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address - /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) + /// @param deployer The address that will deploy the hook. + /// In `forge test`, this will be the test contract `address(this)` or the pranking address + /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) /// @param flags The desired flags for the hook address - /// @param seed Use 0 for as a default. An optional starting salt when linearly searching for a salt. Useful for finding salts for multiple hooks with the same flags + /// @param seed Use 0 for as a default. An optional starting salt when linearly searching for a salt + /// Useful for finding salts for multiple hooks with the same flags /// @param creationCode The creation code of a hook contract. Example: `type(Counter).creationCode` /// @param constructorArgs The encoded constructor arguments of a hook contract. Example: `abi.encode(address(manager))` - /// @return hookAddress salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}()` + /// @return hookAddress salt and corresponding address that was found + /// The salt can be used in `new Hook{salt: salt}()` function find( address deployer, uint160 flags, @@ -43,8 +46,9 @@ library HookMiner { } /// @notice Precompute a contract address deployed via CREATE2 - /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address - /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) + /// @param deployer The address that will deploy the hook + /// In `forge test`, this will be the test contract `address(this)` or the pranking address + /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) /// @param salt The salt used to deploy the hook /// @param creationCode The creation code of a hook contract function computeAddress(address deployer, uint256 salt, bytes memory creationCode) From 5b91cd338b0281bfb9e259f3b8f53fb09924345e Mon Sep 17 00:00:00 2001 From: saucepoint Date: Tue, 10 Oct 2023 22:22:32 -0400 Subject: [PATCH 15/17] update to latest v4-core --- lib/v4-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/v4-core b/lib/v4-core index 73938802..d5fa3b64 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 73938802cad600beb07bd805cc9883e25bf87261 +Subproject commit d5fa3b64a22a81b334497f0f01352bf40ce87d15 From dfbb67088100bbf9b30c441667885603e22baf8c Mon Sep 17 00:00:00 2001 From: saucepoint Date: Tue, 10 Oct 2023 22:35:20 -0400 Subject: [PATCH 16/17] remove safety check --- test/FullRange.t.sol | 4 +--- test/GeomeanOracle.t.sol | 3 +-- test/LimitOrder.t.sol | 4 +--- test/TWAMM.t.sol | 3 +-- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/test/FullRange.t.sol b/test/FullRange.t.sol index cc01ecdd..8952c1ea 100644 --- a/test/FullRange.t.sol +++ b/test/FullRange.t.sol @@ -90,10 +90,8 @@ contract TestFullRange is Test, Deployers, GasSnapshot { uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.BEFORE_SWAP_FLAG); - (address hookAddress, bytes32 salt) = - HookMiner.find(address(this), flags, 0, type(FullRange).creationCode, abi.encode(manager)); + (, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(FullRange).creationCode, abi.encode(manager)); fullRange = new FullRange{salt: salt}(manager); - require(address(fullRange) == hookAddress, "TestFullRange: hook address mismatch"); key = createPoolKey(token0, token1); id = key.toId(); diff --git a/test/GeomeanOracle.t.sol b/test/GeomeanOracle.t.sol index e3304de3..3aa6abbf 100644 --- a/test/GeomeanOracle.t.sol +++ b/test/GeomeanOracle.t.sol @@ -44,10 +44,9 @@ contract TestGeomeanOracle is Test, Deployers, TokenFixture { Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.BEFORE_SWAP_FLAG ); - (address hookAddress, bytes32 salt) = + (, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(GeomeanOracle).creationCode, abi.encode(manager)); geomeanOracle = new GeomeanOracle{salt: salt}(manager); - require(address(geomeanOracle) == hookAddress, "TestGeomeanOracle: hook address mismatch"); vm.warp(1); key = PoolKey(currency0, currency1, 0, MAX_TICK_SPACING, geomeanOracle); diff --git a/test/LimitOrder.t.sol b/test/LimitOrder.t.sol index ff5deaee..d6a7dde9 100644 --- a/test/LimitOrder.t.sol +++ b/test/LimitOrder.t.sol @@ -39,10 +39,8 @@ contract TestLimitOrder is Test, Deployers, TokenFixture { manager = new PoolManager(500000); uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_SWAP_FLAG); - (address hookAddress, bytes32 salt) = - HookMiner.find(address(this), flags, 0, type(LimitOrder).creationCode, abi.encode(manager)); + (, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(LimitOrder).creationCode, abi.encode(manager)); limitOrder = new LimitOrder{salt: salt}(manager); - require(address(limitOrder) == hookAddress, "TestLimitOrder: hook address mismatch"); key = PoolKey(currency0, currency1, 3000, 60, limitOrder); id = key.toId(); diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index a7ccd501..780ff507 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -63,12 +63,11 @@ contract TWAMMTest is Test, Deployers, TokenFixture, GasSnapshot { // Find a salt that produces a hook address with the desired `flags` uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG); - (address hookAddress, bytes32 salt) = + (, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(TWAMM).creationCode, abi.encode(manager, 10_000)); // Deploy hook to the precomputed address using the salt twamm = new TWAMM{salt: salt}(manager, 10_000); - require(address(twamm) == hookAddress, "TWAMMTest: hook address does not match"); // safety check that salt was mined correctly modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); swapRouter = new PoolSwapTest(IPoolManager(address(manager))); From 5f72147d2d2fadd9a1a0bdffe0f3c10de8c45768 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Sat, 16 Mar 2024 18:25:15 +0000 Subject: [PATCH 17/17] update hook miner usage --- test/FullRange.t.sol | 9 ++------ test/GeomeanOracle.t.sol | 14 ++++--------- test/LimitOrder.t.sol | 3 +-- test/TWAMM.t.sol | 6 ++---- test/utils/HookMiner.sol | 45 +++++++++++++++------------------------- 5 files changed, 26 insertions(+), 51 deletions(-) diff --git a/test/FullRange.t.sol b/test/FullRange.t.sol index e6a3c3f8..d6dc02e8 100644 --- a/test/FullRange.t.sol +++ b/test/FullRange.t.sol @@ -64,10 +64,6 @@ contract TestFullRange is Test, Deployers, GasSnapshot { MockERC20 token1; MockERC20 token2; - Currency currency0; - Currency currency1; - - PoolManager manager; FullRange fullRange; PoolId id; @@ -87,9 +83,8 @@ contract TestFullRange is Test, Deployers, GasSnapshot { token1 = tokens[1]; token2 = tokens[2]; - uint160 flags = - uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG | Hooks.BEFORE_SWAP_FLAG); - (, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(FullRange).creationCode, abi.encode(manager)); + uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG | Hooks.BEFORE_SWAP_FLAG); + (, bytes32 salt) = HookMiner.find(address(this), flags, type(FullRange).creationCode, abi.encode(manager)); fullRange = new FullRange{salt: salt}(manager); key = createPoolKey(token0, token1); diff --git a/test/GeomeanOracle.t.sol b/test/GeomeanOracle.t.sol index 2cb41cd3..109761f7 100644 --- a/test/GeomeanOracle.t.sol +++ b/test/GeomeanOracle.t.sol @@ -38,11 +38,10 @@ contract TestGeomeanOracle is Test, Deployers { manager = new PoolManager(500000); uint160 flags = uint160( - Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG - | Hooks.BEFORE_SWAP_FLAG + Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG + | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG | Hooks.BEFORE_SWAP_FLAG ); - (, bytes32 salt) = - HookMiner.find(address(this), flags, 0, type(GeomeanOracle).creationCode, abi.encode(manager)); + (, bytes32 salt) = HookMiner.find(address(this), flags, type(GeomeanOracle).creationCode, abi.encode(manager)); geomeanOracle = new GeomeanOracle{salt: salt}(manager); vm.warp(1); @@ -132,13 +131,8 @@ contract TestGeomeanOracle is Test, Deployers { function testBeforeModifyPositionObservation() public { manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); -<<<<<<< HEAD skip(2); // advance 2 seconds - modifyPositionRouter.modifyPosition( -======= - geomeanOracle.setTime(3); // advance 2 seconds modifyLiquidityRouter.modifyLiquidity( ->>>>>>> main key, IPoolManager.ModifyLiquidityParams( TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000 @@ -198,7 +192,7 @@ contract TestGeomeanOracle is Test, Deployers { function testPermanentLiquidity() public { manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); - geomeanOracle.setTime(3); // advance 2 seconds + skip(2); // advance 2 seconds modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( diff --git a/test/LimitOrder.t.sol b/test/LimitOrder.t.sol index d2576660..7b8ed791 100644 --- a/test/LimitOrder.t.sol +++ b/test/LimitOrder.t.sol @@ -26,7 +26,6 @@ contract TestLimitOrder is Test, Deployers { TestERC20 token0; TestERC20 token1; LimitOrder limitOrder; - PoolKey key; PoolId id; function setUp() public { @@ -38,7 +37,7 @@ contract TestLimitOrder is Test, Deployers { token1 = TestERC20(Currency.unwrap(currency1)); uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_SWAP_FLAG); - (, bytes32 salt) = HookMiner.find(address(this), flags, 0, type(LimitOrder).creationCode, abi.encode(manager)); + (, bytes32 salt) = HookMiner.find(address(this), flags, type(LimitOrder).creationCode, abi.encode(manager)); limitOrder = new LimitOrder{salt: salt}(manager); // key = PoolKey(currency0, currency1, 3000, 60, limitOrder); diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index 8aa87f61..3880d960 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -58,10 +58,8 @@ contract TWAMMTest is Test, Deployers, GasSnapshot { token1 = MockERC20(Currency.unwrap(currency1)); // Find a salt that produces a hook address with the desired `flags` - uint160 flags = - uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_MODIFY_POSITION_FLAG); - (, bytes32 salt) = - HookMiner.find(address(this), flags, 0, type(TWAMM).creationCode, abi.encode(manager, 10_000)); + uint160 flags = uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG); + (, bytes32 salt) = HookMiner.find(address(this), flags, type(TWAMM).creationCode, abi.encode(manager, 10_000)); // Deploy hook to the precomputed address using the salt twamm = new TWAMM{salt: salt}(manager, 10_000); diff --git a/test/utils/HookMiner.sol b/test/utils/HookMiner.sol index f57dbf01..bdeca3fe 100644 --- a/test/utils/HookMiner.sol +++ b/test/utils/HookMiner.sol @@ -1,54 +1,43 @@ // SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.19; +pragma solidity ^0.8.21; /// @title HookMiner - a library for mining hook addresses /// @dev This library is intended for `forge test` environments. There may be gotchas when using salts in `forge script` or `forge create` library HookMiner { - // mask to slice out the top 8 bit of the address - uint160 constant FLAG_MASK = 0xFF << 152; + // mask to slice out the top 12 bit of the address + uint160 constant FLAG_MASK = 0xFFF << 148; // Maximum number of iterations to find a salt, avoid infinite loops - uint256 constant MAX_LOOP = 10_000; + uint256 constant MAX_LOOP = 20_000; /// @notice Find a salt that produces a hook address with the desired `flags` - /// @param deployer The address that will deploy the hook. - /// In `forge test`, this will be the test contract `address(this)` or the pranking address - /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) + /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address + /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) /// @param flags The desired flags for the hook address - /// @param seed Use 0 for as a default. An optional starting salt when linearly searching for a salt - /// Useful for finding salts for multiple hooks with the same flags /// @param creationCode The creation code of a hook contract. Example: `type(Counter).creationCode` /// @param constructorArgs The encoded constructor arguments of a hook contract. Example: `abi.encode(address(manager))` - /// @return hookAddress salt and corresponding address that was found - /// The salt can be used in `new Hook{salt: salt}()` - function find( - address deployer, - uint160 flags, - uint256 seed, - bytes memory creationCode, - bytes memory constructorArgs - ) external pure returns (address, bytes32) { + /// @return hookAddress salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}()` + function find(address deployer, uint160 flags, bytes memory creationCode, bytes memory constructorArgs) + internal + view + returns (address, bytes32) + { address hookAddress; bytes memory creationCodeWithArgs = abi.encodePacked(creationCode, constructorArgs); - uint256 salt = seed; - for (salt; salt < MAX_LOOP;) { + uint256 salt; + for (salt; salt < MAX_LOOP; salt++) { hookAddress = computeAddress(deployer, salt, creationCodeWithArgs); - if (uint160(hookAddress) & FLAG_MASK == flags) { + if (uint160(hookAddress) & FLAG_MASK == flags && hookAddress.code.length == 0) { return (hookAddress, bytes32(salt)); } - - unchecked { - ++salt; - } } revert("HookMiner: could not find salt"); } /// @notice Precompute a contract address deployed via CREATE2 - /// @param deployer The address that will deploy the hook - /// In `forge test`, this will be the test contract `address(this)` or the pranking address - /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) + /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address + /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy) /// @param salt The salt used to deploy the hook /// @param creationCode The creation code of a hook contract function computeAddress(address deployer, uint256 salt, bytes memory creationCode)