From abca37f003889ed6b033091157f91fc478718491 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Sun, 3 Dec 2023 12:29:52 -0500 Subject: [PATCH] static hook fee example --- contracts/src/examples/FixedHookFee.sol | 4 +- src/keywords.json | 7 + src/nav.ts | 21 +-- .../EnableAccessLock.solsnippet | 16 +++ .../fees/fixed-hook-fee/FixedHookFee.sol | 64 +++++++++ .../SetAccessLockPermission.solsnippet | 7 + src/pages/fees/fixed-hook-fee/index.html.ts | 132 ++++++++++++++++++ src/pages/fees/fixed-hook-fee/index.md | 41 ++++++ src/pages/fees/fixed-hook-fee/index.tsx | 29 ++++ src/routes.tsx | 5 + src/search.json | 15 +- 11 files changed, 329 insertions(+), 12 deletions(-) create mode 100644 src/pages/fees/fixed-hook-fee/EnableAccessLock.solsnippet create mode 100644 src/pages/fees/fixed-hook-fee/FixedHookFee.sol create mode 100644 src/pages/fees/fixed-hook-fee/SetAccessLockPermission.solsnippet create mode 100644 src/pages/fees/fixed-hook-fee/index.html.ts create mode 100644 src/pages/fees/fixed-hook-fee/index.md create mode 100644 src/pages/fees/fixed-hook-fee/index.tsx diff --git a/contracts/src/examples/FixedHookFee.sol b/contracts/src/examples/FixedHookFee.sol index 2a2408a81..5a51e3280 100644 --- a/contracts/src/examples/FixedHookFee.sol +++ b/contracts/src/examples/FixedHookFee.sol @@ -30,7 +30,7 @@ contract FixedHookFee is BaseHook { beforeDonate: false, afterDonate: false, noOp: false, - accessLock: true + accessLock: true // -- Required to take a fee -- // }); } @@ -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); diff --git a/src/keywords.json b/src/keywords.json index fa67b2bbc..ff1157594 100644 --- a/src/keywords.json +++ b/src/keywords.json @@ -21,6 +21,13 @@ "swap", "skip swap" ], + "/fees/fixed-hook-fee": [ + "hook", + "hooks", + "fee", + "static fee", + "hook fee" + ], "/create-liquidity": [ "liquidity", "LP", diff --git a/src/nav.ts b/src/nav.ts index f88f2963c..9b07e68d0 100644 --- a/src/nav.ts +++ b/src/nav.ts @@ -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[] = [] @@ -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) => ({ diff --git a/src/pages/fees/fixed-hook-fee/EnableAccessLock.solsnippet b/src/pages/fees/fixed-hook-fee/EnableAccessLock.solsnippet new file mode 100644 index 000000000..60489d7aa --- /dev/null +++ b/src/pages/fees/fixed-hook-fee/EnableAccessLock.solsnippet @@ -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 -- // + }); +} \ No newline at end of file diff --git a/src/pages/fees/fixed-hook-fee/FixedHookFee.sol b/src/pages/fees/fixed-hook-fee/FixedHookFee.sol new file mode 100644 index 000000000..64a86779d --- /dev/null +++ b/src/pages/fees/fixed-hook-fee/FixedHookFee.sol @@ -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); + } +} diff --git a/src/pages/fees/fixed-hook-fee/SetAccessLockPermission.solsnippet b/src/pages/fees/fixed-hook-fee/SetAccessLockPermission.solsnippet new file mode 100644 index 000000000..087d5d187 --- /dev/null +++ b/src/pages/fees/fixed-hook-fee/SetAccessLockPermission.solsnippet @@ -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))); diff --git a/src/pages/fees/fixed-hook-fee/index.html.ts b/src/pages/fees/fixed-hook-fee/index.html.ts new file mode 100644 index 000000000..4a1dd0dce --- /dev/null +++ b/src/pages/fees/fixed-hook-fee/index.html.ts @@ -0,0 +1,132 @@ +// metadata +export const version = "0.8.20" +export const title = "Static Hook Fee" +export const description = "Charge a static hook fee" + +export const keywords = [ + "hook", + "hooks", + "fee", + "static fee", + "hook fee", +] + +export const codes = [ + { + fileName: "EnableAccessLock.sol", + code: "aW1wb3J0IHtIb29rc30gZnJvbSAiQHVuaXN3YXAvdjQtY29yZS9jb250cmFjdHMvbGlicmFyaWVzL0hvb2tzLnNvbCI7CgpmdW5jdGlvbiBnZXRIb29rUGVybWlzc2lvbnMoKSBwdWJsaWMgcHVyZSBvdmVycmlkZSByZXR1cm5zIChIb29rcy5QZXJtaXNzaW9ucyBtZW1vcnkpIHsKICAgIHJldHVybiBIb29rcy5QZXJtaXNzaW9ucyh7CiAgICAgICAgYmVmb3JlSW5pdGlhbGl6ZTogZmFsc2UsCiAgICAgICAgYWZ0ZXJJbml0aWFsaXplOiBmYWxzZSwKICAgICAgICBiZWZvcmVNb2RpZnlQb3NpdGlvbjogZmFsc2UsCiAgICAgICAgYWZ0ZXJNb2RpZnlQb3NpdGlvbjogZmFsc2UsCiAgICAgICAgYmVmb3JlU3dhcDogdHJ1ZSwKICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgIGJlZm9yZURvbmF0ZTogZmFsc2UsCiAgICAgICAgYWZ0ZXJEb25hdGU6IGZhbHNlLAogICAgICAgIG5vT3A6IGZhbHNlLAogICAgICAgIGFjY2Vzc0xvY2s6IHRydWUgLy8gLS0gRU5BQkxFIEFDQ0VTUyBMT0NLIC0tICAvLwogICAgfSk7Cn0=", + }, + { + fileName: "FixedHookFee.sol", + code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiLi4vZm9ya3MvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7SG9va3N9IGZyb20gIkB1bmlzd2FwL3Y0LWNvcmUvY29udHJhY3RzL2xpYnJhcmllcy9Ib29rcy5zb2wiOwppbXBvcnQge0lQb29sTWFuYWdlcn0gZnJvbSAiQHVuaXN3YXAvdjQtY29yZS9jb250cmFjdHMvaW50ZXJmYWNlcy9JUG9vbE1hbmFnZXIuc29sIjsKaW1wb3J0IHtQb29sS2V5fSBmcm9tICJAdW5pc3dhcC92NC1jb3JlL2NvbnRyYWN0cy90eXBlcy9Qb29sS2V5LnNvbCI7CmltcG9ydCB7UG9vbElkLCBQb29sSWRMaWJyYXJ5fSBmcm9tICJAdW5pc3dhcC92NC1jb3JlL2NvbnRyYWN0cy90eXBlcy9Qb29sSWQuc29sIjsKaW1wb3J0IHtCYWxhbmNlRGVsdGF9IGZyb20gIkB1bmlzd2FwL3Y0LWNvcmUvY29udHJhY3RzL3R5cGVzL0JhbGFuY2VEZWx0YS5zb2wiOwppbXBvcnQge0N1cnJlbmN5LCBDdXJyZW5jeUxpYnJhcnl9IGZyb20gIkB1bmlzd2FwL3Y0LWNvcmUvY29udHJhY3RzL3R5cGVzL0N1cnJlbmN5LnNvbCI7Cgpjb250cmFjdCBGaXhlZEhvb2tGZWUgaXMgQmFzZUhvb2sgewogICAgdXNpbmcgUG9vbElkTGlicmFyeSBmb3IgUG9vbEtleTsKICAgIHVzaW5nIEN1cnJlbmN5TGlicmFyeSBmb3IgQ3VycmVuY3k7CgogICAgdWludDI1NiBwdWJsaWMgY29uc3RhbnQgRklYRURfSE9PS19GRUUgPSAwLjAwMDFlMTg7CgogICAgY29uc3RydWN0b3IoSVBvb2xNYW5hZ2VyIF9wb29sTWFuYWdlcikgQmFzZUhvb2soX3Bvb2xNYW5hZ2VyKSB7fQoKICAgIGZ1bmN0aW9uIGdldEhvb2tQZXJtaXNzaW9ucygpIHB1YmxpYyBwdXJlIG92ZXJyaWRlIHJldHVybnMgKEhvb2tzLlBlcm1pc3Npb25zIG1lbW9yeSkgewogICAgICAgIHJldHVybiBIb29rcy5QZXJtaXNzaW9ucyh7CiAgICAgICAgICAgIGJlZm9yZUluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVNb2RpZnlQb3NpdGlvbjogZmFsc2UsCiAgICAgICAgICAgIGFmdGVyTW9kaWZ5UG9zaXRpb246IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVTd2FwOiB0cnVlLAogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IGZhbHNlLAogICAgICAgICAgICBhY2Nlc3NMb2NrOiB0cnVlIC8vIC0tIFJlcXVpcmVkIHRvIHRha2UgYSBmZWUgLS0gLy8KICAgICAgICB9KTsKICAgIH0KCiAgICBmdW5jdGlvbiBiZWZvcmVTd2FwKGFkZHJlc3MsIFBvb2xLZXkgY2FsbGRhdGEga2V5LCBJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSBwYXJhbXMsIGJ5dGVzIGNhbGxkYXRhKQogICAgICAgIGV4dGVybmFsCiAgICAgICAgb3ZlcnJpZGUKICAgICAgICByZXR1cm5zIChieXRlczQpCiAgICB7CiAgICAgICAgLy8gdGFrZSBhIGZpeGVkIGZlZSBvZiAwLjAwMDEgb2YgdGhlIGlucHV0IHRva2VuCiAgICAgICAgcGFyYW1zLnplcm9Gb3JPbmUKICAgICAgICAgICAgPyBwb29sTWFuYWdlci5taW50KGtleS5jdXJyZW5jeTAsIGFkZHJlc3ModGhpcyksIEZJWEVEX0hPT0tfRkVFKQogICAgICAgICAgICA6IHBvb2xNYW5hZ2VyLm1pbnQoa2V5LmN1cnJlbmN5MSwgYWRkcmVzcyh0aGlzKSwgRklYRURfSE9PS19GRUUpOwoKICAgICAgICByZXR1cm4gQmFzZUhvb2suYmVmb3JlU3dhcC5zZWxlY3RvcjsKICAgIH0KCiAgICAvLy8gQGRldiBIb29rIGZlZXMgYXJlIGtlcHQgYXMgUG9vbE1hbmFnZXIgY2xhaW1zLCBzbyBjb2xsZWN0aW5nIEVSQzIwcyB3aWxsIHJlcXVpcmUgbG9ja2luZwogICAgZnVuY3Rpb24gY29sbGVjdEZlZShhZGRyZXNzIHJlY2lwaWVudCwgQ3VycmVuY3kgY3VycmVuY3kpIGV4dGVybmFsIHJldHVybnMgKHVpbnQyNTYgYW1vdW50KSB7CiAgICAgICAgYW1vdW50ID0gYWJpLmRlY29kZShwb29sTWFuYWdlci5sb2NrKGFiaS5lbmNvZGVDYWxsKHRoaXMuaGFuZGxlQ29sbGVjdEZlZSwgKHJlY2lwaWVudCwgY3VycmVuY3kpKSksICh1aW50MjU2KSk7CiAgICB9CgogICAgLy8vIEBkZXYgcmVxdWlyZXMgdGhlIGxvY2sgcGF0dGVybiBpbiBvcmRlciB0byBjYWxsIHBvb2xNYW5hZ2VyLmJ1cm4KICAgIGZ1bmN0aW9uIGhhbmRsZUNvbGxlY3RGZWUoYWRkcmVzcyByZWNpcGllbnQsIEN1cnJlbmN5IGN1cnJlbmN5KSBleHRlcm5hbCByZXR1cm5zICh1aW50MjU2IGFtb3VudCkgewogICAgICAgIC8vIGNvbnZlcnQgdGhlIGZlZSAoQ2xhaW1zKSBpbnRvIEVSQzIwIHRva2VucwogICAgICAgIGFtb3VudCA9IHBvb2xNYW5hZ2VyLmJhbGFuY2VPZihhZGRyZXNzKHRoaXMpLCBjdXJyZW5jeSk7CiAgICAgICAgcG9vbE1hbmFnZXIuYnVybihjdXJyZW5jeSwgYW1vdW50KTsKCiAgICAgICAgLy8gZGlyZWN0IGNsYWltcyAodGhlIHRva2VucykgdG8gdGhlIHJlY2lwaWVudAogICAgICAgIHBvb2xNYW5hZ2VyLnRha2UoY3VycmVuY3ksIHJlY2lwaWVudCwgYW1vdW50KTsKICAgIH0KfQo=", + }, + { + fileName: "SetAccessLockPermission.sol", + code: "Ly8gSG9vayBjYW4gdGFrZSBhIGZlZSB2aWEgQUNDRVNTX0xPQ0sKdWludDE2MCBmbGFncyA9IHVpbnQxNjAoSG9va3MuQkVGT1JFX1NXQVBfRkxBRyB8IEhvb2tzLkFDQ0VTU19MT0NLX0ZMQUcpOwoKKGFkZHJlc3MgaG9va0FkZHJlc3MsIGJ5dGVzMzIgc2FsdCkgPQogICAgSG9va01pbmVyLmZpbmQoYWRkcmVzcyh0aGlzKSwgZmxhZ3MsIHR5cGUoRml4ZWRIb29rRmVlKS5jcmVhdGlvbkNvZGUsIGFiaS5lbmNvZGUoYWRkcmVzcyhtYW5hZ2VyKSkpOwoKaG9vayA9IG5ldyBGaXhlZEhvb2tGZWV7c2FsdDogc2FsdH0oSVBvb2xNYW5hZ2VyKGFkZHJlc3MobWFuYWdlcikpKTsK", + }, +] + +const html = ` +

Optional hook fees are taken (from swappers) via the Access Lock. Hook fees can be dynamically calculated, or simply set to a fixed amount.

+

This example showcases a static fee amount

+
+

Example Static Hook Fee

+

FIXED_HOOK_FEE = 0.0001e18

+

The hook fee is on the input token. If swapAmount = 1e18, the swapper pays 1.0001e18

+
// 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);
+    }
+}
+

Collecting the fee, to recipient alice

+
hook.collectFee(address(alice), Currency.wrap(address(TOKEN)));
+

Enabling Access Lock

+

To allow a hook to call poolManager.mint (fee taking), without a lock, you need to enable the ACCESS_LOCK permission

+
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 --  //
+    });
+}
+

Example permissions during hook deployment:

+
// 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)));
+
` + +export default html diff --git a/src/pages/fees/fixed-hook-fee/index.md b/src/pages/fees/fixed-hook-fee/index.md new file mode 100644 index 000000000..4672e20a6 --- /dev/null +++ b/src/pages/fees/fixed-hook-fee/index.md @@ -0,0 +1,41 @@ +--- +title: Static Hook Fee +version: 0.8.20 +description: Charge a static hook fee +keywords: [hook, hooks, fee, static fee, hook fee] +--- + +- Charge a static hook fee + +Optional hook fees are taken (from swappers) via the Access Lock. Hook fees can be dynamically calculated, or simply set to a fixed amount. + +This example showcases a static fee amount + +--- +## Example Static Hook Fee + +`FIXED_HOOK_FEE = 0.0001e18` + +The hook fee is on the *input* token. If `swapAmount = 1e18`, the swapper pays `1.0001e18` + +```solidity +{{{FixedHookFee}}} +``` + +Collecting the fee, to recipient `alice` +```solidity +hook.collectFee(address(alice), Currency.wrap(address(TOKEN))); +``` + +#### Enabling Access Lock + +To allow a hook to call `poolManager.mint` (fee taking), without a lock, you need to enable the `ACCESS_LOCK` permission + +```solidity +{{{EnableAccessLock}}} +``` + +Example permissions during hook deployment: +```solidity +{{{SetAccessLockPermission}}} +``` diff --git a/src/pages/fees/fixed-hook-fee/index.tsx b/src/pages/fees/fixed-hook-fee/index.tsx new file mode 100644 index 000000000..1c63a4f4a --- /dev/null +++ b/src/pages/fees/fixed-hook-fee/index.tsx @@ -0,0 +1,29 @@ +import React from "react" +import Example from "../../../components/Example" +import html, { version, title, description, codes } from "./index.html" + +interface Path { + path: string + title: string +} + +interface Props { + prev: Path | null + next: Path | null +} + +const ExamplePage: React.FC = ({ prev, next }) => { + return ( + + ) +} + +export default ExamplePage diff --git a/src/routes.tsx b/src/routes.tsx index c4fcb217c..f5bca9068 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,4 +1,5 @@ import component_create_liquidity from "./pages/create-liquidity" +import component_fees_fixed_hook_fee from "./pages/fees/fixed-hook-fee" import component_hooks_no_op from "./pages/hooks/no-op" import component_initialize from "./pages/initialize" import component_swap from "./pages/swap" @@ -25,6 +26,10 @@ const routes: Route[] = [ path: "/create-liquidity", component: component_create_liquidity }, + { + path: "/fees/fixed-hook-fee", + component: component_fees_fixed_hook_fee + }, { path: "/hooks/no-op", component: component_hooks_no_op diff --git a/src/search.json b/src/search.json index 6278a75c1..eac4af144 100644 --- a/src/search.json +++ b/src/search.json @@ -28,10 +28,12 @@ "/initialize" ], "hook": [ - "/hooks/no-op" + "/hooks/no-op", + "/fees/fixed-hook-fee" ], "hooks": [ - "/hooks/no-op" + "/hooks/no-op", + "/fees/fixed-hook-fee" ], "noop": [ "/hooks/no-op" @@ -45,6 +47,15 @@ "skip swap": [ "/hooks/no-op" ], + "fee": [ + "/fees/fixed-hook-fee" + ], + "static fee": [ + "/fees/fixed-hook-fee" + ], + "hook fee": [ + "/fees/fixed-hook-fee" + ], "liquidity": [ "/create-liquidity" ],