Uniswap / v4-periphery

🦄 🦄 🦄 🦄 Peripheral smart contracts for interacting with Uniswap v4
https://blog.uniswap.org/uniswap-v4
GNU General Public License v2.0
708 stars 485 forks source link

Replace implementation+etch pattern with Create2Deployer #59

Closed saucepoint closed 2 months ago

saucepoint commented 1 year ago

Component

General design optimization (improving efficiency, cleanliness, or developer experience)

Describe the suggested feature and problem it solves.

Arachnid's create2 deployer proxy is now available on anvil and forge test by default

This allows us to deterministically mine + deploy arbitrary contracts without the need for a factory or Implementation wrapper that overrides validateHookAddress.

A minor change but it reduces repo clutter+boilerplate by adding consistent abstractions around hook development and testing

Describe the desired implementation.

Currently you need to define an Implementation which overrides the flag checks, and then etch the bytecode to a correctly-flagged address:

       LimitOrder limitOrder = LimitOrder(address(uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_SWAP_FLAG)));
        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));
            }
        }

Abstracting the create2 deployer proxy, we get a much cleaner devex:

        // Deploy the hook to an address with the correct flags
        uint160 flags = uint160(Hooks.AFTER_INITIALIZE_FLAG | Hooks.AFTER_SWAP_FLAG);
        bytes memory hookBytecode = abi.encodePacked(type(LimitOrder).creationCode, abi.encode(address(manager)));
        (, uint256 salt) = HookDeployer.mineSalt(CREATE2_DEPLOYER, flags, hookBytecode);

        LimitOrder limitOrder = new LimitOrder{salt: salt}(address(manager));

Describe alternatives.

N/A

Additional context.

Already works and is available here:

https://github.com/saucepoint/v4-template/blob/main/test/Counter.t.sol#L30-L37

and

https://github.com/saucepoint/v4-template/blob/main/test/utils/HookDeployer.sol


I'm happy to PR and update all the tests to the new pattern!

snreynolds commented 1 year ago

Cool! I think I like this overall. Some thoughts:

tagging @ewilz to see if she has any more thoughts?

saucepoint commented 1 year ago

Wrote a script to check all the ~major chains. Looks like most chains do have Arachnid's deployer proxy at 0x4e59b44847b379578588920cA78FbF26c0B4956C (+ shipped w/ anvil now).

You are correct that this issue is more about testing patterns and infra. Naming the utility library HookDeployer might be a misnomer that I'll change for the PR!

Chain Matching Bytecode
Ethereum
Goerli
Sepolia
Arbitrum One
Arbitrum Goerli
Arbitrum Nova
OP Mainnet
Optimism Goerli
Polygon
Polygon Mumbai
Polygon zkEVM
Polygon zkEVM Testnet
Base
Base Goerli
Avalanche
Avalanche Fuji
Boba Network
BNB Smart Chain
Binance Smart Chain Testnet
Canto
Celo
Alfajores
Cannoli
Fantom
Fantom Testnet
Gnosis
Gnosis Chiado
Linea Mainnet
Linea Goerli Testnet
Mantle
Mantle Testnet
Metis
Metis Goerli
opBNB
opBNB Testnet
zkSync Era
zkSync Era Testnet
Zora
Zora Goerli Testnet
saucepoint commented 2 months ago

given hook flags are now 14 bits, mining is too slow for tests

reference PR for deployCodeTo

https://github.com/Uniswap/v4-periphery/pull/122