A template for writing Uniswap v4 Hooks 🦄
This template provides a starting point for writing Uniswap v4 Hooks, including a simple example and preconfigured test environment. Start by creating a new repository using the "Use this template" button at the top right of this page. Alternatively you can also click this link:
- The example hook Counter.sol demonstrates the
beforeSwap()andafterSwap()hooks - The test template Counter.t.sol preconfigures the v4 pool manager, test tokens, and test liquidity.
Updating to v4-template:latest
This template is actively maintained -- you can update the v4 dependencies, scripts, and helpers:
git remote add template https://github.com/uniswapfoundation/v4-template
git fetch template
git merge template/main <BRANCH> --allow-unrelated-historiesThis template is designed to work with Foundry (stable). If you are using Foundry Nightly, you may encounter compatibility issues. You can update your Foundry installation to the latest stable version by running:
foundryup
To set up the project, run the following commands in your terminal to install dependencies and run the tests:
forge install
forge test
Other than writing unit tests (recommended!), you can only deploy & test hooks on anvil locally. Scripts are available in the script/ directory, which can be used to deploy hooks, create pools, provide liquidity and swap tokens. The scripts support both local anvil environment as well as running them directly on a production network.
- Start Anvil (or fork a specific chain using anvil):
anvilor
anvil --fork-url <YOUR_RPC_URL>- Execute scripts:
forge script script/00_DeployHook.s.sol \
--rpc-url http://localhost:8545 \
--private-key <PRIVATE_KEY> \
--broadcast:::info
It is best to not store your private key even in .env or enter it directly in the command line. Instead use the --account flag to select your private key from your keystore.
:::
- Add your private key to the keystore:
cast wallet import <SET_A_NAME_FOR_KEY> --interactive- You will prompted to enter your private key and set a password, fill and press enter:
Enter private key: <YOUR_PRIVATE_KEY>
Enter keystore password: <SET_NEW_PASSWORD>
You should see this:
`<YOUR_WALLET_PRIVATE_KEY_NAME>` keystore was saved successfully. Address: <YOUR_WALLET_ADDRESS>
::: warning
Use history -c to clear your command history.
:::
- Execute scripts:
forge script script/00_DeployHook.s.sol \
--rpc-url <YOUR_RPC_URL> \
--account <YOUR_WALLET_PRIVATE_KEY_NAME> \
--sender <YOUR_WALLET_ADDRESS> \
--broadcastYou will prompted to enter your wallet password, fill and press enter:
Enter keystore password: <YOUR_PASSWORD>
- Update the
token0andtoken1addresses in theBaseScript.solfile to match the tokens you want to use in the network of your choice for sepolia and mainnet deployments. - Update the
token0Amountandtoken1Amountin theCreatePoolAndAddLiquidity.s.solfile to match the amount of tokens you want to provide liquidity with. - Update the
token0Amountandtoken1Amountin theAddLiquidity.s.solfile to match the amount of tokens you want to provide liquidity with. - Update the
amountInandamountOutMinin theSwap.s.solfile to match the amount of tokens you want to swap.
forge verify-contract \
--rpc-url <URL> \
--chain <CHAIN_NAME_OR_ID> \
# Generally etherscan
--verifier <Verification_Provider> \
# Use --etherscan-api-key <ETHERSCAN_API_KEY> if you are using etherscan
--verifier-api-key <Verification_Provider_API_KEY> \
--constructor-args <ABI_ENCODED_ARGS> \
--num-of-optimizations <OPTIMIZER_RUNS> \
<Contract_Address> \
<path/to/Contract.sol:ContractName>
--watchWhen installing dependencies with forge install, Github may throw a Permission Denied error
Typically caused by missing Github SSH keys, and can be resolved by following the steps here
Or adding the keys to your ssh-agent, if you have already uploaded SSH keys
Some versions of Foundry may limit contract code size to ~25kb, which could prevent local tests to fail. You can resolve this by setting the code-size-limit flag
anvil --code-size-limit 40000
Hook deployment failures are caused by incorrect flags or incorrect salt mining
- Verify the flags are in agreement:
getHookCalls()returns the correct flagsflagsprovided toHookMiner.find(...)
- Verify salt mining is correct:
- In forge test: the deployer for:
new Hook{salt: salt}(...)andHookMiner.find(deployer, ...)are the same. This will beaddress(this). If usingvm.prank, the deployer will be the pranking address - In forge script: the deployer must be the CREATE2 Proxy:
0x4e59b44847b379578588920cA78FbF26c0B4956C- If anvil does not have the CREATE2 deployer, your foundry may be out of date. You can update it with
foundryup
- If anvil does not have the CREATE2 deployer, your foundry may be out of date. You can update it with
- In forge test: the deployer for: