Skip to content

Commit

Permalink
static hook fee example
Browse files Browse the repository at this point in the history
  • Loading branch information
saucepoint committed Dec 3, 2023
1 parent 98f9b5a commit abca37f
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 12 deletions.
4 changes: 2 additions & 2 deletions contracts/src/examples/FixedHookFee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ contract FixedHookFee is BaseHook {
beforeDonate: false,
afterDonate: false,
noOp: false,
accessLock: true
accessLock: true // -- Required to take a fee -- //
});
}

Expand Down Expand Up @@ -60,7 +60,7 @@ contract FixedHookFee is BaseHook {
);
}

/// @dev requires the lock pattern in order to call `poolManager.burn`
/// @dev requires the lock pattern in order to call poolManager.burn
function handleCollectFee(address recipient, Currency currency) external returns (uint256 amount) {
// convert the fee (Claims) into ERC20 tokens
amount = poolManager.balanceOf(address(this), currency);
Expand Down
7 changes: 7 additions & 0 deletions src/keywords.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
"swap",
"skip swap"
],
"/fees/fixed-hook-fee": [
"hook",
"hooks",
"fee",
"static fee",
"hook fee"
],
"/create-liquidity": [
"liquidity",
"LP",
Expand Down
21 changes: 13 additions & 8 deletions src/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ export const HOOK_ROUTES: Route[] = [
}
]

const HACK_ROUTES: Route[] = []
const FEE_ROUTES: Route[] = [
{
path: "fixed-hook-fee",
title: "Static Hook Fee"
}
]

export const TEST_ROUTES: Route[] = []

Expand All @@ -53,13 +58,13 @@ export const ROUTES_BY_CATEGORY = [
path: `/hooks/${route.path}`,
})),
},
// {
// title: "Hacks",
// routes: HACK_ROUTES.map((route) => ({
// ...route,
// path: `/hacks/${route.path}`,
// })),
// },
{
title: "Fees",
routes: FEE_ROUTES.map((route) => ({
...route,
path: `/fees/${route.path}`,
})),
},
// {
// title: "Tests",
// routes: TEST_ROUTES.map((route) => ({
Expand Down
16 changes: 16 additions & 0 deletions src/pages/fees/fixed-hook-fee/EnableAccessLock.solsnippet
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol";

function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeModifyPosition: false,
afterModifyPosition: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
noOp: false,
accessLock: true // -- ENABLE ACCESS LOCK -- //
});
}
64 changes: 64 additions & 0 deletions src/pages/fees/fixed-hook-fee/FixedHookFee.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// TODO: update to v4-periphery/BaseHook.sol when its compatible
import {BaseHook} from "../forks/BaseHook.sol";

import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol";
import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol";
import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol";
import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/contracts/types/PoolId.sol";
import {BalanceDelta} from "@uniswap/v4-core/contracts/types/BalanceDelta.sol";
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/contracts/types/Currency.sol";

contract FixedHookFee is BaseHook {
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;

uint256 public constant FIXED_HOOK_FEE = 0.0001e18;

constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}

function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeModifyPosition: false,
afterModifyPosition: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
noOp: false,
accessLock: true // -- Required to take a fee -- //
});
}

function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)
external
override
returns (bytes4)
{
// take a fixed fee of 0.0001 of the input token
params.zeroForOne
? poolManager.mint(key.currency0, address(this), FIXED_HOOK_FEE)
: poolManager.mint(key.currency1, address(this), FIXED_HOOK_FEE);

return BaseHook.beforeSwap.selector;
}

/// @dev Hook fees are kept as PoolManager claims, so collecting ERC20s will require locking
function collectFee(address recipient, Currency currency) external returns (uint256 amount) {
amount = abi.decode(poolManager.lock(abi.encodeCall(this.handleCollectFee, (recipient, currency))), (uint256));
}

/// @dev requires the lock pattern in order to call poolManager.burn
function handleCollectFee(address recipient, Currency currency) external returns (uint256 amount) {
// convert the fee (Claims) into ERC20 tokens
amount = poolManager.balanceOf(address(this), currency);
poolManager.burn(currency, amount);

// direct claims (the tokens) to the recipient
poolManager.take(currency, recipient, amount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Hook can take a fee via ACCESS_LOCK
uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.ACCESS_LOCK_FLAG);

(address hookAddress, bytes32 salt) =
HookMiner.find(address(this), flags, type(FixedHookFee).creationCode, abi.encode(address(manager)));

hook = new FixedHookFee{salt: salt}(IPoolManager(address(manager)));
Loading

0 comments on commit abca37f

Please sign in to comment.