From 6492c0848b0a7ad1d02d7a7b7d1565539b2e381f Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Dec 2023 17:03:01 -0500 Subject: [PATCH 1/6] initial examples for msg.sender access --- forge-test/GetCurrentLockCaller.t.sol | 115 ++++++++++++++ forge-test/MsgSenderHookData.t.sol | 84 ++++++++++ .../hooks/msg-sender/GetCurrentLockCaller.sol | 52 ++++++ .../hooks/msg-sender/MsgSenderHookData.sol | 50 ++++++ src/pages/hooks/msg-sender/index.html.ts | 148 ++++++++++++++++++ src/pages/hooks/msg-sender/index.md | 40 +++++ src/pages/hooks/msg-sender/index.tsx | 29 ++++ 7 files changed, 518 insertions(+) create mode 100644 forge-test/GetCurrentLockCaller.t.sol create mode 100644 forge-test/MsgSenderHookData.t.sol create mode 100644 src/pages/hooks/msg-sender/GetCurrentLockCaller.sol create mode 100644 src/pages/hooks/msg-sender/MsgSenderHookData.sol create mode 100644 src/pages/hooks/msg-sender/index.html.ts create mode 100644 src/pages/hooks/msg-sender/index.md create mode 100644 src/pages/hooks/msg-sender/index.tsx diff --git a/forge-test/GetCurrentLockCaller.t.sol b/forge-test/GetCurrentLockCaller.t.sol new file mode 100644 index 000000000..62b428b2d --- /dev/null +++ b/forge-test/GetCurrentLockCaller.t.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import {IHooks} from "v4-core/interfaces/IHooks.sol"; +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {TickMath} from "v4-core/libraries/TickMath.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol"; +import {PoolKey} from "v4-core/types/PoolKey.sol"; +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; +import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; +import {Constants} from "v4-core/../test/utils/Constants.sol"; +import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol"; +import {HookTest} from "./utils/HookTest.sol"; +import {GetCurrentLockCaller} from "src/pages/hooks/msg-sender/GetCurrentLockCaller.sol"; +import {HookMiner} from "./utils/HookMiner.sol"; + +contract GetCurrentLockCallerTest is HookTest { + using PoolIdLibrary for PoolKey; + using CurrencyLibrary for Currency; + + GetCurrentLockCaller counter; + PoolKey poolKey; + PoolId poolId; + + address alice = makeAddr("alice"); + + function setUp() public { + // creates the pool manager, test tokens, and other utility routers + HookTest.initHookTestEnv(); + + // Deploy the hook to an address with the correct flags + uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG); + (address hookAddress, bytes32 salt) = + HookMiner.find(address(this), flags, type(GetCurrentLockCaller).creationCode, abi.encode(address(manager))); + counter = new GetCurrentLockCaller{salt: salt}(IPoolManager(address(manager))); + require(address(counter) == hookAddress, "GetCurrentLockCallerTest: hook address mismatch"); + + // Create the pool + poolKey = PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 3000, 60, IHooks(counter)); + poolId = poolKey.toId(); + initializeRouter.initialize(poolKey, Constants.SQRT_RATIO_1_1, ZERO_BYTES); + + // Provide liquidity to the pool + modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-60, 60, 10 ether), ZERO_BYTES); + modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-120, 120, 10 ether), ZERO_BYTES); + modifyPositionRouter.modifyPosition( + poolKey, + IPoolManager.ModifyPositionParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), + ZERO_BYTES + ); + } + + function test_getCurrentLockCaller() public { + counter.setAllowedUser(alice, true); + token0.mint(alice, 100 ether); + + vm.startPrank(alice); + token0.approve(address(swapRouter), type(uint256).max); + + // Perform a test swap // + int256 amount = 1e18; + bool zeroForOne = true; + lockSwap(poolKey, amount, zeroForOne, ZERO_BYTES, false); + // ------------------- // + vm.stopPrank(); + } + + function test_getCurrentLockCallerRevert() public { + counter.setAllowedUser(alice, false); + token0.mint(alice, 100 ether); + + vm.startPrank(alice); + token0.approve(address(swapRouter), type(uint256).max); + + // Perform a test swap // + int256 amount = 1e18; + bool zeroForOne = true; + // vm.expectRevert(); + lockSwap(poolKey, amount, zeroForOne, ZERO_BYTES, true); + // ------------------- // + vm.stopPrank(); + } + + function lockSwap(PoolKey memory key, int256 amountSpecified, bool zeroForOne, bytes memory hookData, bool expectRevert) + internal + returns (BalanceDelta swapDelta) + { + IPoolManager.SwapParams memory params = IPoolManager.SwapParams({ + zeroForOne: zeroForOne, + amountSpecified: amountSpecified, + sqrtPriceLimitX96: zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT // unlimited impact + }); + + PoolSwapTest.TestSettings memory testSettings = + PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true, currencyAlreadySent: false}); + + bytes memory result; + if (expectRevert) { + vm.expectRevert(); + result = manager.lock( + address(swapRouter), + abi.encode(PoolSwapTest.CallbackData(address(this), testSettings, key, params, hookData)) + ); + } else { + result = manager.lock( + address(swapRouter), + abi.encode(PoolSwapTest.CallbackData(address(this), testSettings, key, params, hookData)) + ); + } + + swapDelta = abi.decode(result, (BalanceDelta)); + } +} diff --git a/forge-test/MsgSenderHookData.t.sol b/forge-test/MsgSenderHookData.t.sol new file mode 100644 index 000000000..dcb2962d2 --- /dev/null +++ b/forge-test/MsgSenderHookData.t.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import {IHooks} from "v4-core/interfaces/IHooks.sol"; +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {TickMath} from "v4-core/libraries/TickMath.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {PoolKey} from "v4-core/types/PoolKey.sol"; +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; +import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; +import {Constants} from "v4-core/../test/utils/Constants.sol"; +import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol"; +import {HookTest} from "./utils/HookTest.sol"; +import {MsgSenderHookData} from "src/pages/hooks/msg-sender/MsgSenderHookData.sol"; +import {HookMiner} from "./utils/HookMiner.sol"; + +contract MsgSenderHookDataTest is HookTest { + using PoolIdLibrary for PoolKey; + using CurrencyLibrary for Currency; + + MsgSenderHookData counter; + PoolKey poolKey; + PoolId poolId; + + address alice = makeAddr("alice"); + + function setUp() public { + // creates the pool manager, test tokens, and other utility routers + HookTest.initHookTestEnv(); + + // Deploy the hook to an address with the correct flags + uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG); + (address hookAddress, bytes32 salt) = + HookMiner.find(address(this), flags, type(MsgSenderHookData).creationCode, abi.encode(address(manager))); + counter = new MsgSenderHookData{salt: salt}(IPoolManager(address(manager))); + require(address(counter) == hookAddress, "MsgSenderHookDataTest: hook address mismatch"); + + // Create the pool + poolKey = PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 3000, 60, IHooks(counter)); + poolId = poolKey.toId(); + initializeRouter.initialize(poolKey, Constants.SQRT_RATIO_1_1, ZERO_BYTES); + + // Provide liquidity to the pool + modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-60, 60, 10 ether), ZERO_BYTES); + modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-120, 120, 10 ether), ZERO_BYTES); + modifyPositionRouter.modifyPosition( + poolKey, + IPoolManager.ModifyPositionParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), + ZERO_BYTES + ); + } + + function test_msgSenderHookData() public { + counter.setAllowedUser(alice, true); + token0.mint(alice, 100 ether); + + vm.startPrank(alice); + token0.approve(address(swapRouter), type(uint256).max); + + // Perform a test swap // + int256 amount = 1e18; + bool zeroForOne = true; + swap(poolKey, amount, zeroForOne, abi.encode(alice)); + // ------------------- // + vm.stopPrank(); + } + + function test_msgSenderRevert() public { + counter.setAllowedUser(alice, false); + token0.mint(alice, 100 ether); + + vm.startPrank(alice); + token0.approve(address(swapRouter), type(uint256).max); + + // Perform a test swap // + int256 amount = 1e18; + bool zeroForOne = true; + vm.expectRevert(); + swap(poolKey, amount, zeroForOne, abi.encode(alice)); + // ------------------- // + vm.stopPrank(); + } +} diff --git a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol new file mode 100644 index 000000000..db80e5d82 --- /dev/null +++ b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// TODO: update to v4-periphery/BaseHook.sol when its compatible +import {BaseHook} from "@v4-by-example/utils/BaseHook.sol"; + +import {console2} from "forge-std/console2.sol"; +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {PoolKey} from "v4-core/types/PoolKey.sol"; +import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; +import {Lockers} from "v4-core/libraries/Lockers.sol"; + +contract GetCurrentLockCaller is BaseHook { + using PoolIdLibrary for PoolKey; + + mapping(address user => bool allowed) public allowedUsers; + + 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: false + }); + } + + function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) + external + override + returns (bytes4) + { + // --- Read the user's address --- // + (, address user) = poolManager.getLock(1); + require(allowedUsers[user], "GetCurrentLockCaller: User not allowed"); + return BaseHook.beforeSwap.selector; + } + + // Helper function for demonstration + function setAllowedUser(address user, bool allowed) external { + allowedUsers[user] = allowed; + } +} diff --git a/src/pages/hooks/msg-sender/MsgSenderHookData.sol b/src/pages/hooks/msg-sender/MsgSenderHookData.sol new file mode 100644 index 000000000..1e30ed259 --- /dev/null +++ b/src/pages/hooks/msg-sender/MsgSenderHookData.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// TODO: update to v4-periphery/BaseHook.sol when its compatible +import {BaseHook} from "@v4-by-example/utils/BaseHook.sol"; + +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {PoolKey} from "v4-core/types/PoolKey.sol"; +import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; + +contract MsgSenderHookData is BaseHook { + using PoolIdLibrary for PoolKey; + + mapping(address user => bool allowed) public allowedUsers; + + 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: false + }); + } + + function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) + external + override + returns (bytes4) + { + // --- Read the user's address --- // + address user = abi.decode(hookData, (address)); + require(allowedUsers[user], "MsgSenderHookData: User not allowed"); + return BaseHook.beforeSwap.selector; + } + + // Helper function for demonstration + function setAllowedUser(address user, bool allowed) external { + allowedUsers[user] = allowed; + } +} diff --git a/src/pages/hooks/msg-sender/index.html.ts b/src/pages/hooks/msg-sender/index.html.ts new file mode 100644 index 000000000..30adb1542 --- /dev/null +++ b/src/pages/hooks/msg-sender/index.html.ts @@ -0,0 +1,148 @@ +// metadata +export const version = "0.8.20" +export const title = "Custom Curve" +export const description = "Replace v3 concentrated liquidity curve" + +export const keywords = [ + "hook", + "hooks", + "noop", + "no-op", + "custom curve", + "custom accounting", +] + +export const codes = [ + { + fileName: "CustomCurve.sol", + code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCi8vIFRPRE86IHJlcGxhY2Ugd2l0aCB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gY29tcGF0aWJpbGl0eSBpcyBmaXhlZAppbXBvcnQge0Jhc2VIb29rfSBmcm9tICJAdjQtYnktZXhhbXBsZS91dGlscy9CYXNlSG9vay5zb2wiOwoKaW1wb3J0IHtIb29rc30gZnJvbSAidjQtY29yZS9saWJyYXJpZXMvSG9va3Muc29sIjsKaW1wb3J0IHtJUG9vbE1hbmFnZXJ9IGZyb20gInY0LWNvcmUvaW50ZXJmYWNlcy9JUG9vbE1hbmFnZXIuc29sIjsKaW1wb3J0IHtQb29sS2V5fSBmcm9tICJ2NC1jb3JlL3R5cGVzL1Bvb2xLZXkuc29sIjsKaW1wb3J0IHtQb29sSWQsIFBvb2xJZExpYnJhcnl9IGZyb20gInY0LWNvcmUvdHlwZXMvUG9vbElkLnNvbCI7CmltcG9ydCB7Q3VycmVuY3ksIEN1cnJlbmN5TGlicmFyeX0gZnJvbSAidjQtY29yZS90eXBlcy9DdXJyZW5jeS5zb2wiOwoKaW1wb3J0IHtJRVJDMjB9IGZyb20gImZvcmdlLXN0ZC9pbnRlcmZhY2VzL0lFUkMyMC5zb2wiOwoKY29udHJhY3QgQ3VzdG9tQ3VydmUgaXMgQmFzZUhvb2sgewogICAgdXNpbmcgUG9vbElkTGlicmFyeSBmb3IgUG9vbEtleTsKICAgIHVzaW5nIEN1cnJlbmN5TGlicmFyeSBmb3IgQ3VycmVuY3k7CgogICAgY29uc3RydWN0b3IoSVBvb2xNYW5hZ2VyIF9wb29sTWFuYWdlcikgQmFzZUhvb2soX3Bvb2xNYW5hZ2VyKSB7fQoKICAgIGZ1bmN0aW9uIGdldEhvb2tQZXJtaXNzaW9ucygpIHB1YmxpYyBwdXJlIG92ZXJyaWRlIHJldHVybnMgKEhvb2tzLlBlcm1pc3Npb25zIG1lbW9yeSkgewogICAgICAgIHJldHVybiBIb29rcy5QZXJtaXNzaW9ucyh7CiAgICAgICAgICAgIGJlZm9yZUluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVNb2RpZnlQb3NpdGlvbjogdHJ1ZSwgLy8gLS0gZGlzYWJsZSB2NCBsaXF1aWRpdHkgd2l0aCBhIHJldmVydCAtLSAvLwogICAgICAgICAgICBhZnRlck1vZGlmeVBvc2l0aW9uOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlU3dhcDogdHJ1ZSwgLy8gLS0gTm8tb3AnaW5nIHRoZSBzd2FwIC0tICAvLwogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IHRydWUsIC8vIC0tIEVOQUJMRSBOTy1PUCAtLSAgLy8KICAgICAgICAgICAgYWNjZXNzTG9jazogdHJ1ZSAvLyAtLSBFTkFCTEUgQ1VTVE9NIENVUlZFUyAtLSAvLwogICAgICAgIH0pOwogICAgfQoKICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvLwogICAgLy8gTGlxdWlkaXR5IEZ1bmN0aW9ucyAobm90IHByb2R1Y3Rpb24gcmVhZHkpIC8vCiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy8KICAgIC8vLyBAbm90aWNlIEFkZCBsaXF1aWRpdHkgZm9yIHRoZSBjdXN0b20gY3VydmUKICAgIC8vLyBAcGFyYW0ga2V5IFBvb2xLZXkgb2YgdGhlIHBvb2wgdG8gYWRkIGxpcXVpZGl0eSB0bwogICAgLy8vIEBwYXJhbSBsaXF1aWRpdHlEZWx0YSBBbW91bnQgb2YgbGlxdWlkaXR5IHRvIGFkZAogICAgZnVuY3Rpb24gYWRkTGlxdWlkaXR5KFBvb2xLZXkgY2FsbGRhdGEga2V5LCB1aW50MjU2IGxpcXVpZGl0eURlbHRhKSBleHRlcm5hbCB7CiAgICAgICAgLy8gQGRldjogVXBkYXRlIHRoaXMKICAgICAgICAvLyBHaXZlbiBzcG90IHByaWNlIGFuZCB0aGUgY3VzdG9tIGN1cnZlLCBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIHRva2VucyB0byBhZGQKICAgICAgICB1aW50MjU2IHRva2VuMEluOwogICAgICAgIHVpbnQyNTYgdG9rZW4xSW47CgogICAgICAgIC8vIHRyYW5zZmVyIHRva2VucyB0byBob29rLCB0byBhY3QgYXMgbGlxdWlkaXR5IGZvciBzd2FwcwogICAgICAgIElFUkMyMChDdXJyZW5jeS51bndyYXAoa2V5LmN1cnJlbmN5MCkpLnRyYW5zZmVyRnJvbShtc2cuc2VuZGVyLCBhZGRyZXNzKHRoaXMpLCB0b2tlbjBJbik7CiAgICAgICAgSUVSQzIwKEN1cnJlbmN5LnVud3JhcChrZXkuY3VycmVuY3kxKSkudHJhbnNmZXJGcm9tKG1zZy5zZW5kZXIsIGFkZHJlc3ModGhpcyksIHRva2VuMUluKTsKCiAgICAgICAgLy8gVE9ETzogcHJvZHVjdGlvbi1yZWFkeSByZXF1aXJlcyBtaW50aW5nIGEgcmVjZWlwdCB0b2tlbiBldGMKICAgIH0KCiAgICAvLy8gQG5vdGljZSBDYWxjdWxhdGUgdGhlIGFtb3VudCBvZiB0b2tlbnMgcGFpZCBieSB0aGUgc3dhcHBlcgogICAgLy8vIEBwYXJhbSBwYXJhbXMgU3dhcFBhcmFtcyBwYXNzZWQgdG8gdGhlIHN3YXAgZnVuY3Rpb24KICAgIC8vLyBAcmV0dXJuIFRoZSBhbW91bnQgb2YgdG9rZW5zIHBhaWQgYnkgdGhlIHN3YXBwZXIKICAgIGZ1bmN0aW9uIGdldFRva2VuSW5BbW91bnQoSVBvb2xNYW5hZ2VyLlN3YXBQYXJhbXMgY2FsbGRhdGEgcGFyYW1zKSBwdWJsaWMgcHVyZSByZXR1cm5zICh1aW50MjU2KSB7CiAgICAgICAgcmV0dXJuIDFlMTg7CiAgICB9CgogICAgLy8vIEBub3RpY2UgQ2FsY3VsYXRlIHRoZSBhbW91bnQgb2YgdG9rZW5zIHNlbnQgdG8gdGhlIHN3YXBwZXIKICAgIC8vLyBAcGFyYW0gcGFyYW1zIFN3YXBQYXJhbXMgcGFzc2VkIHRvIHRoZSBzd2FwIGZ1bmN0aW9uCiAgICAvLy8gQHJldHVybiBUaGUgYW1vdW50IG9mIHRva2VucyBzZW50IHRvIHRoZSBzd2FwcGVyCiAgICBmdW5jdGlvbiBnZXRUb2tlbk91dEFtb3VudChJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSBwYXJhbXMpIHB1YmxpYyBwdXJlIHJldHVybnMgKHVpbnQyNTYpIHsKICAgICAgICByZXR1cm4gMWUxODsKICAgIH0KCiAgICBmdW5jdGlvbiBiZWZvcmVTd2FwKGFkZHJlc3MsIFBvb2xLZXkgY2FsbGRhdGEga2V5LCBJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSBwYXJhbXMsIGJ5dGVzIGNhbGxkYXRhKQogICAgICAgIGV4dGVybmFsCiAgICAgICAgb3ZlcnJpZGUKICAgICAgICByZXR1cm5zIChieXRlczQpCiAgICB7CiAgICAgICAgLy8gY2FsY3VsYXRlIHRoZSBhbW91bnQgb2YgdG9rZW5zLCBiYXNlZCBvbiBhIGN1c3RvbSBjdXJ2ZQogICAgICAgIHVpbnQyNTYgdG9rZW5JbkFtb3VudCA9IGdldFRva2VuSW5BbW91bnQocGFyYW1zKTsgLy8gYW1vdW50IG9mIHRva2VucyBwYWlkIGJ5IHRoZSBzd2FwcGVyCiAgICAgICAgdWludDI1NiB0b2tlbk91dEFtb3VudCA9IGdldFRva2VuT3V0QW1vdW50KHBhcmFtcyk7IC8vIGFtb3VudCBvZiB0b2tlbnMgc2VudCB0byB0aGUgc3dhcHBlcgoKICAgICAgICAvLyBkZXRlcm1pbmUgaW5ib3VuZC9vdXRib3VuZCB0b2tlbiBiYXNlZCBvbiAwLT4xIG9yIDEtPjAgc3dhcAogICAgICAgIChDdXJyZW5jeSBpbmJvdW5kLCBDdXJyZW5jeSBvdXRib3VuZCkgPQogICAgICAgICAgICBwYXJhbXMuemVyb0Zvck9uZSA/IChrZXkuY3VycmVuY3kwLCBrZXkuY3VycmVuY3kxKSA6IChrZXkuY3VycmVuY3kxLCBrZXkuY3VycmVuY3kwKTsKCiAgICAgICAgLy8gaW5ib3VuZCB0b2tlbiBpcyBhZGRlZCB0byBob29rJ3MgcmVzZXJ2ZXMsIGRlYnQgcGFpZCBieSB0aGUgc3dhcHBlcgogICAgICAgIHBvb2xNYW5hZ2VyLnRha2UoaW5ib3VuZCwgYWRkcmVzcyh0aGlzKSwgdG9rZW5JbkFtb3VudCk7CgogICAgICAgIC8vIG91dGJvdW5kIHRva2VuIGlzIHJlbW92ZWQgZnJvbSBob29rJ3MgcmVzZXJ2ZXMsIGFuZCBzZW50IHRvIHRoZSBzd2FwcGVyCiAgICAgICAgb3V0Ym91bmQudHJhbnNmZXIoYWRkcmVzcyhwb29sTWFuYWdlciksIHRva2VuT3V0QW1vdW50KTsKICAgICAgICBwb29sTWFuYWdlci5zZXR0bGUob3V0Ym91bmQpOwoKICAgICAgICAvLyBwcmV2ZW50IG5vcm1hbCB2NCBzd2FwIGxvZ2ljIGZyb20gZXhlY3V0aW5nCiAgICAgICAgcmV0dXJuIEhvb2tzLk5PX09QX1NFTEVDVE9SOwogICAgfQoKICAgIC8vLyBAbm90aWNlIE5vIGxpcXVpZGl0eSB3aWxsIGJlIG1hbmFnZWQgYnkgdjQgUG9vbE1hbmFnZXIKICAgIGZ1bmN0aW9uIGJlZm9yZU1vZGlmeVBvc2l0aW9uKAogICAgICAgIGFkZHJlc3MsCiAgICAgICAgUG9vbEtleSBjYWxsZGF0YSBrZXksCiAgICAgICAgSVBvb2xNYW5hZ2VyLk1vZGlmeVBvc2l0aW9uUGFyYW1zIGNhbGxkYXRhLAogICAgICAgIGJ5dGVzIGNhbGxkYXRhCiAgICApIGV4dGVybmFsIG92ZXJyaWRlIHJldHVybnMgKGJ5dGVzNCkgewogICAgICAgIHJldmVydCgiTm8gdjQgTGlxdWlkaXR5IGFsbG93ZWQiKTsKICAgIH0KfQo=", + }, +] + +const html = ` +

In v4, hooks can swap on any curve, formula, or arbitrary logic (offchain quoters). Custom curves may include:

+ +

Custom curves will require NoOp to skip v3 swap math

+

By creating credits and debits through the PoolManager, the official Uniswap router can route through custom curves!

+
+

Custom Curve Template

+

To get started with a custom curve, simply implement getTokenIn(), getTokenOut(), and addLiquidity()

+
// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+// TODO: replace with v4-periphery/BaseHook.sol when compatibility is fixed
+import {BaseHook} from "@v4-by-example/utils/BaseHook.sol";
+
+import {Hooks} from "v4-core/libraries/Hooks.sol";
+import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
+import {PoolKey} from "v4-core/types/PoolKey.sol";
+import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
+import {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol";
+
+import {IERC20} from "forge-std/interfaces/IERC20.sol";
+
+contract CustomCurve is BaseHook {
+    using PoolIdLibrary for PoolKey;
+    using CurrencyLibrary for Currency;
+
+    constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}
+
+    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
+        return Hooks.Permissions({
+            beforeInitialize: false,
+            afterInitialize: false,
+            beforeModifyPosition: true, // -- disable v4 liquidity with a revert -- //
+            afterModifyPosition: false,
+            beforeSwap: true, // -- No-op'ing the swap --  //
+            afterSwap: false,
+            beforeDonate: false,
+            afterDonate: false,
+            noOp: true, // -- ENABLE NO-OP --  //
+            accessLock: true // -- ENABLE CUSTOM CURVES -- //
+        });
+    }
+
+    // ------------------------------------------ //
+    // Liquidity Functions (not production ready) //
+    // ------------------------------------------ //
+    /// @notice Add liquidity for the custom curve
+    /// @param key PoolKey of the pool to add liquidity to
+    /// @param liquidityDelta Amount of liquidity to add
+    function addLiquidity(PoolKey calldata key, uint256 liquidityDelta) external {
+        // @dev: Update this
+        // Given spot price and the custom curve, calculate the ratio of tokens to add
+        uint256 token0In;
+        uint256 token1In;
+
+        // transfer tokens to hook, to act as liquidity for swaps
+        IERC20(Currency.unwrap(key.currency0)).transferFrom(msg.sender, address(this), token0In);
+        IERC20(Currency.unwrap(key.currency1)).transferFrom(msg.sender, address(this), token1In);
+
+        // TODO: production-ready requires minting a receipt token etc
+    }
+
+    /// @notice Calculate the amount of tokens paid by the swapper
+    /// @param params SwapParams passed to the swap function
+    /// @return The amount of tokens paid by the swapper
+    function getTokenInAmount(IPoolManager.SwapParams calldata params) public pure returns (uint256) {
+        return 1e18;
+    }
+
+    /// @notice Calculate the amount of tokens sent to the swapper
+    /// @param params SwapParams passed to the swap function
+    /// @return The amount of tokens sent to the swapper
+    function getTokenOutAmount(IPoolManager.SwapParams calldata params) public pure returns (uint256) {
+        return 1e18;
+    }
+
+    function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)
+        external
+        override
+        returns (bytes4)
+    {
+        // calculate the amount of tokens, based on a custom curve
+        uint256 tokenInAmount = getTokenInAmount(params); // amount of tokens paid by the swapper
+        uint256 tokenOutAmount = getTokenOutAmount(params); // amount of tokens sent to the swapper
+
+        // determine inbound/outbound token based on 0->1 or 1->0 swap
+        (Currency inbound, Currency outbound) =
+            params.zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);
+
+        // inbound token is added to hook's reserves, debt paid by the swapper
+        poolManager.take(inbound, address(this), tokenInAmount);
+
+        // outbound token is removed from hook's reserves, and sent to the swapper
+        outbound.transfer(address(poolManager), tokenOutAmount);
+        poolManager.settle(outbound);
+
+        // prevent normal v4 swap logic from executing
+        return Hooks.NO_OP_SELECTOR;
+    }
+
+    /// @notice No liquidity will be managed by v4 PoolManager
+    function beforeModifyPosition(
+        address,
+        PoolKey calldata key,
+        IPoolManager.ModifyPositionParams calldata,
+        bytes calldata
+    ) external override returns (bytes4) {
+        revert("No v4 Liquidity allowed");
+    }
+}
+

A note on testing

+ +

For an end-to-end complete example of testing a custom curve, please see constant-sum

+` + +export default html diff --git a/src/pages/hooks/msg-sender/index.md b/src/pages/hooks/msg-sender/index.md new file mode 100644 index 000000000..7a45624a3 --- /dev/null +++ b/src/pages/hooks/msg-sender/index.md @@ -0,0 +1,40 @@ +--- +title: Access `msg.sender` +version: 0.8.20 +description: Access msg.sender within a hook +keywords: [hook, hooks, msg.sender, msgsender, sender] +--- + +Please note accessing `msg.sender` inside a hook has multiple solutions -- each solution includes different tradeoffs + +## `bytes memory hookData` - provide the caller as arbitrary data to the periphery router + +Callers of periphery contracts (`PoolSwapTest`) should provide the user's address in the `bytes memory hookData` argument + +* Tradeoff: Routing and/or user interfaces will need to be aware of this + +## Get Current Lock Caller - save the initial lock caller by using the `PoolManager` to invoke a periphery router + +Callers of `poolManager.lock` will specify their periphery router (`PoolSwapTest`). The calleris saved to the `Lockers` data structure and accessed with `Lockers.getCurrentLockCaller()` + +* Tradeoff: the transaction entrypoint is `poolManager.lock` and not `PoolSwapTest.swap` (nonconventional UX) + +--- + +## `bytes memory hookData` + +Provide the user's address to the `PoolSwapTest` +```solidity +IPoolManager.SwapParams memory params; + +PoolSwapTest.TestSettings memory testSettings; + +bytes memory hookData = abi.encode(address(USER_ADDRESS)); +swapRouter.swap(key, params, testSettings, hookData); +``` + +```solidity +{{{MsgSenderHookData}}} +``` + +## Get Current Lock Target diff --git a/src/pages/hooks/msg-sender/index.tsx b/src/pages/hooks/msg-sender/index.tsx new file mode 100644 index 000000000..1c63a4f4a --- /dev/null +++ b/src/pages/hooks/msg-sender/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 From 3317bd254b534e00f76fadbbcf7b82e6ce2a4ccd Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Dec 2023 15:01:24 -0500 Subject: [PATCH 2/6] formatting --- .../hooks/msg-sender/GetCurrentLockCaller.sol | 3 - .../msg-sender/PoolManagerLock.solsnippet | 6 + .../PoolSwapTestHookData.solsnippet | 7 + src/pages/hooks/msg-sender/index.html.ts | 199 ++++++++++-------- src/pages/hooks/msg-sender/index.md | 37 ++-- 5 files changed, 143 insertions(+), 109 deletions(-) create mode 100644 src/pages/hooks/msg-sender/PoolManagerLock.solsnippet create mode 100644 src/pages/hooks/msg-sender/PoolSwapTestHookData.solsnippet diff --git a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol index db80e5d82..bfc7c3668 100644 --- a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol +++ b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol @@ -8,13 +8,10 @@ import {console2} from "forge-std/console2.sol"; import {Hooks} from "v4-core/libraries/Hooks.sol"; import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; import {PoolKey} from "v4-core/types/PoolKey.sol"; -import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; import {Lockers} from "v4-core/libraries/Lockers.sol"; contract GetCurrentLockCaller is BaseHook { - using PoolIdLibrary for PoolKey; - mapping(address user => bool allowed) public allowedUsers; constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} diff --git a/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet b/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet new file mode 100644 index 000000000..e59e507b3 --- /dev/null +++ b/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet @@ -0,0 +1,6 @@ +// User directly locks on the PoolManager +vm.prank(alice); +manager.lock( + address(swapRouter), + abi.encode(PoolSwapTest.CallbackData(address(this), testSettings, key, params, hookData)) +); \ No newline at end of file diff --git a/src/pages/hooks/msg-sender/PoolSwapTestHookData.solsnippet b/src/pages/hooks/msg-sender/PoolSwapTestHookData.solsnippet new file mode 100644 index 000000000..df129a887 --- /dev/null +++ b/src/pages/hooks/msg-sender/PoolSwapTestHookData.solsnippet @@ -0,0 +1,7 @@ +IPoolManager.SwapParams memory params = ...; + +PoolSwapTest.TestSettings memory testSettings = ...; + +// provide the user's address as hookData to be available inside the hook function +bytes memory hookData = abi.encode(address(USER_ADDRESS)); +swapRouter.swap(key, params, testSettings, hookData); \ No newline at end of file diff --git a/src/pages/hooks/msg-sender/index.html.ts b/src/pages/hooks/msg-sender/index.html.ts index 30adb1542..d6e308476 100644 --- a/src/pages/hooks/msg-sender/index.html.ts +++ b/src/pages/hooks/msg-sender/index.html.ts @@ -1,55 +1,73 @@ // metadata export const version = "0.8.20" -export const title = "Custom Curve" -export const description = "Replace v3 concentrated liquidity curve" +export const title = "Access msg.sender within a Hook" +export const description = "Access msg.sender within a hook" export const keywords = [ "hook", "hooks", - "noop", - "no-op", - "custom curve", - "custom accounting", + "msg.sender", + "msgsender", + "sender", ] export const codes = [ { - fileName: "CustomCurve.sol", - code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yMDsKCi8vIFRPRE86IHJlcGxhY2Ugd2l0aCB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gY29tcGF0aWJpbGl0eSBpcyBmaXhlZAppbXBvcnQge0Jhc2VIb29rfSBmcm9tICJAdjQtYnktZXhhbXBsZS91dGlscy9CYXNlSG9vay5zb2wiOwoKaW1wb3J0IHtIb29rc30gZnJvbSAidjQtY29yZS9saWJyYXJpZXMvSG9va3Muc29sIjsKaW1wb3J0IHtJUG9vbE1hbmFnZXJ9IGZyb20gInY0LWNvcmUvaW50ZXJmYWNlcy9JUG9vbE1hbmFnZXIuc29sIjsKaW1wb3J0IHtQb29sS2V5fSBmcm9tICJ2NC1jb3JlL3R5cGVzL1Bvb2xLZXkuc29sIjsKaW1wb3J0IHtQb29sSWQsIFBvb2xJZExpYnJhcnl9IGZyb20gInY0LWNvcmUvdHlwZXMvUG9vbElkLnNvbCI7CmltcG9ydCB7Q3VycmVuY3ksIEN1cnJlbmN5TGlicmFyeX0gZnJvbSAidjQtY29yZS90eXBlcy9DdXJyZW5jeS5zb2wiOwoKaW1wb3J0IHtJRVJDMjB9IGZyb20gImZvcmdlLXN0ZC9pbnRlcmZhY2VzL0lFUkMyMC5zb2wiOwoKY29udHJhY3QgQ3VzdG9tQ3VydmUgaXMgQmFzZUhvb2sgewogICAgdXNpbmcgUG9vbElkTGlicmFyeSBmb3IgUG9vbEtleTsKICAgIHVzaW5nIEN1cnJlbmN5TGlicmFyeSBmb3IgQ3VycmVuY3k7CgogICAgY29uc3RydWN0b3IoSVBvb2xNYW5hZ2VyIF9wb29sTWFuYWdlcikgQmFzZUhvb2soX3Bvb2xNYW5hZ2VyKSB7fQoKICAgIGZ1bmN0aW9uIGdldEhvb2tQZXJtaXNzaW9ucygpIHB1YmxpYyBwdXJlIG92ZXJyaWRlIHJldHVybnMgKEhvb2tzLlBlcm1pc3Npb25zIG1lbW9yeSkgewogICAgICAgIHJldHVybiBIb29rcy5QZXJtaXNzaW9ucyh7CiAgICAgICAgICAgIGJlZm9yZUluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVNb2RpZnlQb3NpdGlvbjogdHJ1ZSwgLy8gLS0gZGlzYWJsZSB2NCBsaXF1aWRpdHkgd2l0aCBhIHJldmVydCAtLSAvLwogICAgICAgICAgICBhZnRlck1vZGlmeVBvc2l0aW9uOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlU3dhcDogdHJ1ZSwgLy8gLS0gTm8tb3AnaW5nIHRoZSBzd2FwIC0tICAvLwogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IHRydWUsIC8vIC0tIEVOQUJMRSBOTy1PUCAtLSAgLy8KICAgICAgICAgICAgYWNjZXNzTG9jazogdHJ1ZSAvLyAtLSBFTkFCTEUgQ1VTVE9NIENVUlZFUyAtLSAvLwogICAgICAgIH0pOwogICAgfQoKICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvLwogICAgLy8gTGlxdWlkaXR5IEZ1bmN0aW9ucyAobm90IHByb2R1Y3Rpb24gcmVhZHkpIC8vCiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy8KICAgIC8vLyBAbm90aWNlIEFkZCBsaXF1aWRpdHkgZm9yIHRoZSBjdXN0b20gY3VydmUKICAgIC8vLyBAcGFyYW0ga2V5IFBvb2xLZXkgb2YgdGhlIHBvb2wgdG8gYWRkIGxpcXVpZGl0eSB0bwogICAgLy8vIEBwYXJhbSBsaXF1aWRpdHlEZWx0YSBBbW91bnQgb2YgbGlxdWlkaXR5IHRvIGFkZAogICAgZnVuY3Rpb24gYWRkTGlxdWlkaXR5KFBvb2xLZXkgY2FsbGRhdGEga2V5LCB1aW50MjU2IGxpcXVpZGl0eURlbHRhKSBleHRlcm5hbCB7CiAgICAgICAgLy8gQGRldjogVXBkYXRlIHRoaXMKICAgICAgICAvLyBHaXZlbiBzcG90IHByaWNlIGFuZCB0aGUgY3VzdG9tIGN1cnZlLCBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIHRva2VucyB0byBhZGQKICAgICAgICB1aW50MjU2IHRva2VuMEluOwogICAgICAgIHVpbnQyNTYgdG9rZW4xSW47CgogICAgICAgIC8vIHRyYW5zZmVyIHRva2VucyB0byBob29rLCB0byBhY3QgYXMgbGlxdWlkaXR5IGZvciBzd2FwcwogICAgICAgIElFUkMyMChDdXJyZW5jeS51bndyYXAoa2V5LmN1cnJlbmN5MCkpLnRyYW5zZmVyRnJvbShtc2cuc2VuZGVyLCBhZGRyZXNzKHRoaXMpLCB0b2tlbjBJbik7CiAgICAgICAgSUVSQzIwKEN1cnJlbmN5LnVud3JhcChrZXkuY3VycmVuY3kxKSkudHJhbnNmZXJGcm9tKG1zZy5zZW5kZXIsIGFkZHJlc3ModGhpcyksIHRva2VuMUluKTsKCiAgICAgICAgLy8gVE9ETzogcHJvZHVjdGlvbi1yZWFkeSByZXF1aXJlcyBtaW50aW5nIGEgcmVjZWlwdCB0b2tlbiBldGMKICAgIH0KCiAgICAvLy8gQG5vdGljZSBDYWxjdWxhdGUgdGhlIGFtb3VudCBvZiB0b2tlbnMgcGFpZCBieSB0aGUgc3dhcHBlcgogICAgLy8vIEBwYXJhbSBwYXJhbXMgU3dhcFBhcmFtcyBwYXNzZWQgdG8gdGhlIHN3YXAgZnVuY3Rpb24KICAgIC8vLyBAcmV0dXJuIFRoZSBhbW91bnQgb2YgdG9rZW5zIHBhaWQgYnkgdGhlIHN3YXBwZXIKICAgIGZ1bmN0aW9uIGdldFRva2VuSW5BbW91bnQoSVBvb2xNYW5hZ2VyLlN3YXBQYXJhbXMgY2FsbGRhdGEgcGFyYW1zKSBwdWJsaWMgcHVyZSByZXR1cm5zICh1aW50MjU2KSB7CiAgICAgICAgcmV0dXJuIDFlMTg7CiAgICB9CgogICAgLy8vIEBub3RpY2UgQ2FsY3VsYXRlIHRoZSBhbW91bnQgb2YgdG9rZW5zIHNlbnQgdG8gdGhlIHN3YXBwZXIKICAgIC8vLyBAcGFyYW0gcGFyYW1zIFN3YXBQYXJhbXMgcGFzc2VkIHRvIHRoZSBzd2FwIGZ1bmN0aW9uCiAgICAvLy8gQHJldHVybiBUaGUgYW1vdW50IG9mIHRva2VucyBzZW50IHRvIHRoZSBzd2FwcGVyCiAgICBmdW5jdGlvbiBnZXRUb2tlbk91dEFtb3VudChJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSBwYXJhbXMpIHB1YmxpYyBwdXJlIHJldHVybnMgKHVpbnQyNTYpIHsKICAgICAgICByZXR1cm4gMWUxODsKICAgIH0KCiAgICBmdW5jdGlvbiBiZWZvcmVTd2FwKGFkZHJlc3MsIFBvb2xLZXkgY2FsbGRhdGEga2V5LCBJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSBwYXJhbXMsIGJ5dGVzIGNhbGxkYXRhKQogICAgICAgIGV4dGVybmFsCiAgICAgICAgb3ZlcnJpZGUKICAgICAgICByZXR1cm5zIChieXRlczQpCiAgICB7CiAgICAgICAgLy8gY2FsY3VsYXRlIHRoZSBhbW91bnQgb2YgdG9rZW5zLCBiYXNlZCBvbiBhIGN1c3RvbSBjdXJ2ZQogICAgICAgIHVpbnQyNTYgdG9rZW5JbkFtb3VudCA9IGdldFRva2VuSW5BbW91bnQocGFyYW1zKTsgLy8gYW1vdW50IG9mIHRva2VucyBwYWlkIGJ5IHRoZSBzd2FwcGVyCiAgICAgICAgdWludDI1NiB0b2tlbk91dEFtb3VudCA9IGdldFRva2VuT3V0QW1vdW50KHBhcmFtcyk7IC8vIGFtb3VudCBvZiB0b2tlbnMgc2VudCB0byB0aGUgc3dhcHBlcgoKICAgICAgICAvLyBkZXRlcm1pbmUgaW5ib3VuZC9vdXRib3VuZCB0b2tlbiBiYXNlZCBvbiAwLT4xIG9yIDEtPjAgc3dhcAogICAgICAgIChDdXJyZW5jeSBpbmJvdW5kLCBDdXJyZW5jeSBvdXRib3VuZCkgPQogICAgICAgICAgICBwYXJhbXMuemVyb0Zvck9uZSA/IChrZXkuY3VycmVuY3kwLCBrZXkuY3VycmVuY3kxKSA6IChrZXkuY3VycmVuY3kxLCBrZXkuY3VycmVuY3kwKTsKCiAgICAgICAgLy8gaW5ib3VuZCB0b2tlbiBpcyBhZGRlZCB0byBob29rJ3MgcmVzZXJ2ZXMsIGRlYnQgcGFpZCBieSB0aGUgc3dhcHBlcgogICAgICAgIHBvb2xNYW5hZ2VyLnRha2UoaW5ib3VuZCwgYWRkcmVzcyh0aGlzKSwgdG9rZW5JbkFtb3VudCk7CgogICAgICAgIC8vIG91dGJvdW5kIHRva2VuIGlzIHJlbW92ZWQgZnJvbSBob29rJ3MgcmVzZXJ2ZXMsIGFuZCBzZW50IHRvIHRoZSBzd2FwcGVyCiAgICAgICAgb3V0Ym91bmQudHJhbnNmZXIoYWRkcmVzcyhwb29sTWFuYWdlciksIHRva2VuT3V0QW1vdW50KTsKICAgICAgICBwb29sTWFuYWdlci5zZXR0bGUob3V0Ym91bmQpOwoKICAgICAgICAvLyBwcmV2ZW50IG5vcm1hbCB2NCBzd2FwIGxvZ2ljIGZyb20gZXhlY3V0aW5nCiAgICAgICAgcmV0dXJuIEhvb2tzLk5PX09QX1NFTEVDVE9SOwogICAgfQoKICAgIC8vLyBAbm90aWNlIE5vIGxpcXVpZGl0eSB3aWxsIGJlIG1hbmFnZWQgYnkgdjQgUG9vbE1hbmFnZXIKICAgIGZ1bmN0aW9uIGJlZm9yZU1vZGlmeVBvc2l0aW9uKAogICAgICAgIGFkZHJlc3MsCiAgICAgICAgUG9vbEtleSBjYWxsZGF0YSBrZXksCiAgICAgICAgSVBvb2xNYW5hZ2VyLk1vZGlmeVBvc2l0aW9uUGFyYW1zIGNhbGxkYXRhLAogICAgICAgIGJ5dGVzIGNhbGxkYXRhCiAgICApIGV4dGVybmFsIG92ZXJyaWRlIHJldHVybnMgKGJ5dGVzNCkgewogICAgICAgIHJldmVydCgiTm8gdjQgTGlxdWlkaXR5IGFsbG93ZWQiKTsKICAgIH0KfQo=", + fileName: "GetCurrentLockCaller.sol", + code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiQHY0LWJ5LWV4YW1wbGUvdXRpbHMvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7Y29uc29sZTJ9IGZyb20gImZvcmdlLXN0ZC9jb25zb2xlMi5zb2wiOwppbXBvcnQge0hvb2tzfSBmcm9tICJ2NC1jb3JlL2xpYnJhcmllcy9Ib29rcy5zb2wiOwppbXBvcnQge0lQb29sTWFuYWdlcn0gZnJvbSAidjQtY29yZS9pbnRlcmZhY2VzL0lQb29sTWFuYWdlci5zb2wiOwppbXBvcnQge1Bvb2xLZXl9IGZyb20gInY0LWNvcmUvdHlwZXMvUG9vbEtleS5zb2wiOwppbXBvcnQge0JhbGFuY2VEZWx0YX0gZnJvbSAidjQtY29yZS90eXBlcy9CYWxhbmNlRGVsdGEuc29sIjsKaW1wb3J0IHtMb2NrZXJzfSBmcm9tICJ2NC1jb3JlL2xpYnJhcmllcy9Mb2NrZXJzLnNvbCI7Cgpjb250cmFjdCBHZXRDdXJyZW50TG9ja0NhbGxlciBpcyBCYXNlSG9vayB7CiAgICBtYXBwaW5nKGFkZHJlc3MgdXNlciA9PiBib29sIGFsbG93ZWQpIHB1YmxpYyBhbGxvd2VkVXNlcnM7CgogICAgY29uc3RydWN0b3IoSVBvb2xNYW5hZ2VyIF9wb29sTWFuYWdlcikgQmFzZUhvb2soX3Bvb2xNYW5hZ2VyKSB7fQoKICAgIGZ1bmN0aW9uIGdldEhvb2tQZXJtaXNzaW9ucygpIHB1YmxpYyBwdXJlIG92ZXJyaWRlIHJldHVybnMgKEhvb2tzLlBlcm1pc3Npb25zIG1lbW9yeSkgewogICAgICAgIHJldHVybiBIb29rcy5QZXJtaXNzaW9ucyh7CiAgICAgICAgICAgIGJlZm9yZUluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVNb2RpZnlQb3NpdGlvbjogZmFsc2UsCiAgICAgICAgICAgIGFmdGVyTW9kaWZ5UG9zaXRpb246IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVTd2FwOiB0cnVlLAogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IGZhbHNlLAogICAgICAgICAgICBhY2Nlc3NMb2NrOiBmYWxzZQogICAgICAgIH0pOwogICAgfQoKICAgIGZ1bmN0aW9uIGJlZm9yZVN3YXAoYWRkcmVzcywgUG9vbEtleSBjYWxsZGF0YSBrZXksIElQb29sTWFuYWdlci5Td2FwUGFyYW1zIGNhbGxkYXRhLCBieXRlcyBjYWxsZGF0YSBob29rRGF0YSkKICAgICAgICBleHRlcm5hbAogICAgICAgIG92ZXJyaWRlCiAgICAgICAgcmV0dXJucyAoYnl0ZXM0KQogICAgewogICAgICAgIC8vIC0tLSBSZWFkIHRoZSB1c2VyJ3MgYWRkcmVzcyAtLS0gLy8KICAgICAgICAoLCBhZGRyZXNzIHVzZXIpID0gcG9vbE1hbmFnZXIuZ2V0TG9jaygxKTsKICAgICAgICByZXF1aXJlKGFsbG93ZWRVc2Vyc1t1c2VyXSwgIkdldEN1cnJlbnRMb2NrQ2FsbGVyOiBVc2VyIG5vdCBhbGxvd2VkIik7CiAgICAgICAgcmV0dXJuIEJhc2VIb29rLmJlZm9yZVN3YXAuc2VsZWN0b3I7CiAgICB9CgogICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBkZW1vbnN0cmF0aW9uCiAgICBmdW5jdGlvbiBzZXRBbGxvd2VkVXNlcihhZGRyZXNzIHVzZXIsIGJvb2wgYWxsb3dlZCkgZXh0ZXJuYWwgewogICAgICAgIGFsbG93ZWRVc2Vyc1t1c2VyXSA9IGFsbG93ZWQ7CiAgICB9Cn0K", + }, + { + fileName: "MsgSenderHookData.sol", + code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiQHY0LWJ5LWV4YW1wbGUvdXRpbHMvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7SG9va3N9IGZyb20gInY0LWNvcmUvbGlicmFyaWVzL0hvb2tzLnNvbCI7CmltcG9ydCB7SVBvb2xNYW5hZ2VyfSBmcm9tICJ2NC1jb3JlL2ludGVyZmFjZXMvSVBvb2xNYW5hZ2VyLnNvbCI7CmltcG9ydCB7UG9vbEtleX0gZnJvbSAidjQtY29yZS90eXBlcy9Qb29sS2V5LnNvbCI7CmltcG9ydCB7UG9vbElkLCBQb29sSWRMaWJyYXJ5fSBmcm9tICJ2NC1jb3JlL3R5cGVzL1Bvb2xJZC5zb2wiOwppbXBvcnQge0JhbGFuY2VEZWx0YX0gZnJvbSAidjQtY29yZS90eXBlcy9CYWxhbmNlRGVsdGEuc29sIjsKCmNvbnRyYWN0IE1zZ1NlbmRlckhvb2tEYXRhIGlzIEJhc2VIb29rIHsKICAgIHVzaW5nIFBvb2xJZExpYnJhcnkgZm9yIFBvb2xLZXk7CgogICAgbWFwcGluZyhhZGRyZXNzIHVzZXIgPT4gYm9vbCBhbGxvd2VkKSBwdWJsaWMgYWxsb3dlZFVzZXJzOwoKICAgIGNvbnN0cnVjdG9yKElQb29sTWFuYWdlciBfcG9vbE1hbmFnZXIpIEJhc2VIb29rKF9wb29sTWFuYWdlcikge30KCiAgICBmdW5jdGlvbiBnZXRIb29rUGVybWlzc2lvbnMoKSBwdWJsaWMgcHVyZSBvdmVycmlkZSByZXR1cm5zIChIb29rcy5QZXJtaXNzaW9ucyBtZW1vcnkpIHsKICAgICAgICByZXR1cm4gSG9va3MuUGVybWlzc2lvbnMoewogICAgICAgICAgICBiZWZvcmVJbml0aWFsaXplOiBmYWxzZSwKICAgICAgICAgICAgYWZ0ZXJJbml0aWFsaXplOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlTW9kaWZ5UG9zaXRpb246IGZhbHNlLAogICAgICAgICAgICBhZnRlck1vZGlmeVBvc2l0aW9uOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlU3dhcDogdHJ1ZSwKICAgICAgICAgICAgYWZ0ZXJTd2FwOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlRG9uYXRlOiBmYWxzZSwKICAgICAgICAgICAgYWZ0ZXJEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBub09wOiBmYWxzZSwKICAgICAgICAgICAgYWNjZXNzTG9jazogZmFsc2UKICAgICAgICB9KTsKICAgIH0KCiAgICBmdW5jdGlvbiBiZWZvcmVTd2FwKGFkZHJlc3MsIFBvb2xLZXkgY2FsbGRhdGEga2V5LCBJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSwgYnl0ZXMgY2FsbGRhdGEgaG9va0RhdGEpCiAgICAgICAgZXh0ZXJuYWwKICAgICAgICBvdmVycmlkZQogICAgICAgIHJldHVybnMgKGJ5dGVzNCkKICAgIHsKICAgICAgICAvLyAtLS0gUmVhZCB0aGUgdXNlcidzIGFkZHJlc3MgLS0tIC8vCiAgICAgICAgYWRkcmVzcyB1c2VyID0gYWJpLmRlY29kZShob29rRGF0YSwgKGFkZHJlc3MpKTsKICAgICAgICByZXF1aXJlKGFsbG93ZWRVc2Vyc1t1c2VyXSwgIk1zZ1NlbmRlckhvb2tEYXRhOiBVc2VyIG5vdCBhbGxvd2VkIik7CiAgICAgICAgcmV0dXJuIEJhc2VIb29rLmJlZm9yZVN3YXAuc2VsZWN0b3I7CiAgICB9CgogICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBkZW1vbnN0cmF0aW9uCiAgICBmdW5jdGlvbiBzZXRBbGxvd2VkVXNlcihhZGRyZXNzIHVzZXIsIGJvb2wgYWxsb3dlZCkgZXh0ZXJuYWwgewogICAgICAgIGFsbG93ZWRVc2Vyc1t1c2VyXSA9IGFsbG93ZWQ7CiAgICB9Cn0K", + }, + { + fileName: "PoolManagerLock.sol", + code: "Ly8gVXNlciBkaXJlY3RseSBsb2NrcyBvbiB0aGUgUG9vbE1hbmFnZXIKdm0ucHJhbmsoYWxpY2UpOwptYW5hZ2VyLmxvY2soCiAgICBhZGRyZXNzKHN3YXBSb3V0ZXIpLAogICAgYWJpLmVuY29kZShQb29sU3dhcFRlc3QuQ2FsbGJhY2tEYXRhKGFkZHJlc3ModGhpcyksIHRlc3RTZXR0aW5ncywga2V5LCBwYXJhbXMsIGhvb2tEYXRhKSkKKTs=", + }, + { + fileName: "PoolSwapTestHookData.sol", + code: "SVBvb2xNYW5hZ2VyLlN3YXBQYXJhbXMgbWVtb3J5IHBhcmFtcyA9IC4uLjsKClBvb2xTd2FwVGVzdC5UZXN0U2V0dGluZ3MgbWVtb3J5IHRlc3RTZXR0aW5ncyA9IC4uLjsKCi8vIHByb3ZpZGUgdGhlIHVzZXIncyBhZGRyZXNzIGFzIGhvb2tEYXRhIHRvIGJlIGF2YWlsYWJsZSBpbnNpZGUgdGhlIGhvb2sgZnVuY3Rpb24KYnl0ZXMgbWVtb3J5IGhvb2tEYXRhID0gYWJpLmVuY29kZShhZGRyZXNzKFVTRVJfQUREUkVTUykpOwpzd2FwUm91dGVyLnN3YXAoa2V5LCBwYXJhbXMsIHRlc3RTZXR0aW5ncywgaG9va0RhdGEpOw==", }, ] -const html = `
    -
  • Replace the v3 concentrated liquidity curve with your own
  • +const html = `

    Please note there are multiple solutions -- each includes different tradeoffs

    +

    Using hookData

    +

    Callers (EOAs / contracts / multisigs) of periphery contracts (PoolSwapTest) can provide the user's address as the hookData argument

    +
      +
    • Tradeoff: Routing, quoters, and user interfaces will need to be aware of this non-standard parameter
    -

    In v4, hooks can swap on any curve, formula, or arbitrary logic (offchain quoters). Custom curves may include:

    +

    Get Lock Caller

    +

    Callers can use the PoolManager to invoke a periphery router. The lock caller is saved in transient storage and can be accessed within a hook

      -
    • Constant-sum: always swap tokens 1:1
    • -
    • StableSwap
    • -
    • LAMMbert
    • +
    • Tradeoff: the transaction entrypoint is poolManager.lock and not PoolSwapTest.swap, leading to unconventional UX
    -

    Custom curves will require NoOp to skip v3 swap math

    -

    By creating credits and debits through the PoolManager, the official Uniswap router can route through custom curves!


    -

    Custom Curve Template

    -

    To get started with a custom curve, simply implement getTokenIn(), getTokenOut(), and addLiquidity()

    +

    bytes memory hookData

    +

    Provide the user's address to the PoolSwapTest

    +
    IPoolManager.SwapParams memory params = ...;
    +
    +PoolSwapTest.TestSettings memory testSettings = ...;
    +
    +// provide the user's address as hookData to be available inside the hook function
    +bytes memory hookData = abi.encode(address(USER_ADDRESS));
    +swapRouter.swap(key, params, testSettings, hookData);
    +

    Decode the hookData into an address type

    // SPDX-License-Identifier: MIT
    -pragma solidity ^0.8.20;
    +pragma solidity ^0.8.19;
     
    -// TODO: replace with v4-periphery/BaseHook.sol when compatibility is fixed
    +// TODO: update to v4-periphery/BaseHook.sol when its compatible
     import {BaseHook} from "@v4-by-example/utils/BaseHook.sol";
     
     import {Hooks} from "v4-core/libraries/Hooks.sol";
     import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
     import {PoolKey} from "v4-core/types/PoolKey.sol";
     import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol";
    -import {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol";
    +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
     
    -import {IERC20} from "forge-std/interfaces/IERC20.sol";
    -
    -contract CustomCurve is BaseHook {
    +contract MsgSenderHookData is BaseHook {
         using PoolIdLibrary for PoolKey;
    -    using CurrencyLibrary for Currency;
    +
    +    mapping(address user => bool allowed) public allowedUsers;
     
         constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}
     
    @@ -57,92 +75,91 @@ const html = `
      return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, - beforeModifyPosition: true, // -- disable v4 liquidity with a revert -- // + beforeModifyPosition: false, afterModifyPosition: false, - beforeSwap: true, // -- No-op'ing the swap -- // + beforeSwap: true, afterSwap: false, beforeDonate: false, afterDonate: false, - noOp: true, // -- ENABLE NO-OP -- // - accessLock: true // -- ENABLE CUSTOM CURVES -- // + noOp: false, + accessLock: false }); } - // ------------------------------------------ // - // Liquidity Functions (not production ready) // - // ------------------------------------------ // - /// @notice Add liquidity for the custom curve - /// @param key PoolKey of the pool to add liquidity to - /// @param liquidityDelta Amount of liquidity to add - function addLiquidity(PoolKey calldata key, uint256 liquidityDelta) external { - // @dev: Update this - // Given spot price and the custom curve, calculate the ratio of tokens to add - uint256 token0In; - uint256 token1In; - - // transfer tokens to hook, to act as liquidity for swaps - IERC20(Currency.unwrap(key.currency0)).transferFrom(msg.sender, address(this), token0In); - IERC20(Currency.unwrap(key.currency1)).transferFrom(msg.sender, address(this), token1In); - - // TODO: production-ready requires minting a receipt token etc + function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) + external + override + returns (bytes4) + { + // --- Read the user's address --- // + address user = abi.decode(hookData, (address)); + require(allowedUsers[user], "MsgSenderHookData: User not allowed"); + return BaseHook.beforeSwap.selector; } - /// @notice Calculate the amount of tokens paid by the swapper - /// @param params SwapParams passed to the swap function - /// @return The amount of tokens paid by the swapper - function getTokenInAmount(IPoolManager.SwapParams calldata params) public pure returns (uint256) { - return 1e18; + // Helper function for demonstration + function setAllowedUser(address user, bool allowed) external { + allowedUsers[user] = allowed; } +} +

    Get Lock Caller

    +

    Invoke poolManager.lock with the user, and specify the PoolSwapTest router

    +
    // User directly locks on the PoolManager
    +vm.prank(alice);
    +manager.lock(
    +    address(swapRouter),
    +    abi.encode(PoolSwapTest.CallbackData(address(this), testSettings, key, params, hookData))
    +);
    +

    Access the Lock Caller via poolManager.getLock()

    +
    // SPDX-License-Identifier: MIT
    +pragma solidity ^0.8.19;
    +
    +// TODO: update to v4-periphery/BaseHook.sol when its compatible
    +import {BaseHook} from "@v4-by-example/utils/BaseHook.sol";
     
    -    /// @notice Calculate the amount of tokens sent to the swapper
    -    /// @param params SwapParams passed to the swap function
    -    /// @return The amount of tokens sent to the swapper
    -    function getTokenOutAmount(IPoolManager.SwapParams calldata params) public pure returns (uint256) {
    -        return 1e18;
    +import {console2} from "forge-std/console2.sol";
    +import {Hooks} from "v4-core/libraries/Hooks.sol";
    +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
    +import {PoolKey} from "v4-core/types/PoolKey.sol";
    +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
    +import {Lockers} from "v4-core/libraries/Lockers.sol";
    +
    +contract GetCurrentLockCaller is BaseHook {
    +    mapping(address user => bool allowed) public allowedUsers;
    +
    +    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: false
    +        });
         }
     
    -    function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)
    +    function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData)
             external
             override
             returns (bytes4)
         {
    -        // calculate the amount of tokens, based on a custom curve
    -        uint256 tokenInAmount = getTokenInAmount(params); // amount of tokens paid by the swapper
    -        uint256 tokenOutAmount = getTokenOutAmount(params); // amount of tokens sent to the swapper
    -
    -        // determine inbound/outbound token based on 0->1 or 1->0 swap
    -        (Currency inbound, Currency outbound) =
    -            params.zeroForOne ? (key.currency0, key.currency1) : (key.currency1, key.currency0);
    -
    -        // inbound token is added to hook's reserves, debt paid by the swapper
    -        poolManager.take(inbound, address(this), tokenInAmount);
    -
    -        // outbound token is removed from hook's reserves, and sent to the swapper
    -        outbound.transfer(address(poolManager), tokenOutAmount);
    -        poolManager.settle(outbound);
    -
    -        // prevent normal v4 swap logic from executing
    -        return Hooks.NO_OP_SELECTOR;
    +        // --- Read the user's address --- //
    +        (, address user) = poolManager.getLock(1);
    +        require(allowedUsers[user], "GetCurrentLockCaller: User not allowed");
    +        return BaseHook.beforeSwap.selector;
         }
     
    -    /// @notice No liquidity will be managed by v4 PoolManager
    -    function beforeModifyPosition(
    -        address,
    -        PoolKey calldata key,
    -        IPoolManager.ModifyPositionParams calldata,
    -        bytes calldata
    -    ) external override returns (bytes4) {
    -        revert("No v4 Liquidity allowed");
    +    // Helper function for demonstration
    +    function setAllowedUser(address user, bool allowed) external {
    +        allowedUsers[user] = allowed;
         }
     }
    -

    A note on testing

    -
      -
    • Custom curves will require taking tokens from PoolManager. You should seed some liquidity on the hookless pool seen here

      -
    • -
    • Custom curves should support both exact-amount-in and exact-amount-out. You can see an example test here

      -
    • -
    -

    For an end-to-end complete example of testing a custom curve, please see constant-sum

    -` +` export default html diff --git a/src/pages/hooks/msg-sender/index.md b/src/pages/hooks/msg-sender/index.md index 7a45624a3..47351fa94 100644 --- a/src/pages/hooks/msg-sender/index.md +++ b/src/pages/hooks/msg-sender/index.md @@ -1,23 +1,23 @@ --- -title: Access `msg.sender` +title: Access msg.sender within a Hook version: 0.8.20 description: Access msg.sender within a hook keywords: [hook, hooks, msg.sender, msgsender, sender] --- -Please note accessing `msg.sender` inside a hook has multiple solutions -- each solution includes different tradeoffs +Please note there are multiple solutions -- each includes different tradeoffs -## `bytes memory hookData` - provide the caller as arbitrary data to the periphery router +### Using `hookData` -Callers of periphery contracts (`PoolSwapTest`) should provide the user's address in the `bytes memory hookData` argument +Callers (EOAs / contracts / multisigs) of periphery contracts (`PoolSwapTest`) can provide the user's address as the `hookData` argument -* Tradeoff: Routing and/or user interfaces will need to be aware of this +* Tradeoff: Routing, quoters, and user interfaces will need to be aware of this non-standard parameter -## Get Current Lock Caller - save the initial lock caller by using the `PoolManager` to invoke a periphery router +### Get Lock Caller -Callers of `poolManager.lock` will specify their periphery router (`PoolSwapTest`). The calleris saved to the `Lockers` data structure and accessed with `Lockers.getCurrentLockCaller()` +Callers can use the `PoolManager` to invoke a periphery router. The lock caller is saved in transient storage and can be accessed within a hook -* Tradeoff: the transaction entrypoint is `poolManager.lock` and not `PoolSwapTest.swap` (nonconventional UX) +* Tradeoff: the transaction entrypoint is `poolManager.lock` and not `PoolSwapTest.swap`, leading to unconventional UX --- @@ -25,16 +25,23 @@ Callers of `poolManager.lock` will specify their periphery router (`PoolSwapTest Provide the user's address to the `PoolSwapTest` ```solidity -IPoolManager.SwapParams memory params; - -PoolSwapTest.TestSettings memory testSettings; - -bytes memory hookData = abi.encode(address(USER_ADDRESS)); -swapRouter.swap(key, params, testSettings, hookData); +{{{PoolSwapTestHookData}}} ``` +Decode the `hookData` into an `address` type ```solidity {{{MsgSenderHookData}}} ``` -## Get Current Lock Target +## Get Lock Caller + +Invoke `poolManager.lock` with the user, and specify the `PoolSwapTest` router +```solidity +{{{PoolManagerLock}}} +``` + +Access the Lock Caller via `poolManager.getLock()` + +```solidity +{{{GetCurrentLockCaller}}} +``` From c0ec3dbcbe1dd79ead6559126050089455439fd8 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Dec 2023 15:01:32 -0500 Subject: [PATCH 3/6] routes and search index --- src/keywords.json | 7 +++++++ src/nav.ts | 4 ++++ src/routes.tsx | 5 +++++ src/search.json | 11 +++++++++++ 4 files changed, 27 insertions(+) diff --git a/src/keywords.json b/src/keywords.json index 59c90b59d..460d8862e 100644 --- a/src/keywords.json +++ b/src/keywords.json @@ -21,6 +21,13 @@ "swap", "skip swap" ], + "/hooks/msg-sender": [ + "hook", + "hooks", + "msg.sender", + "msgsender", + "sender" + ], "/hooks/custom-curve": [ "hook", "hooks", diff --git a/src/nav.ts b/src/nav.ts index f85edf8bb..dcd3f0e4c 100644 --- a/src/nav.ts +++ b/src/nav.ts @@ -33,6 +33,10 @@ export const HOOK_ROUTES: Route[] = [ { path: "custom-curve", title: "Custom Curve" + }, + { + path: "msg-sender", + title: "Access msg.sender" } ] diff --git a/src/routes.tsx b/src/routes.tsx index 26a7e4406..4ad4f3d9e 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,6 +1,7 @@ import component_create_liquidity from "./pages/create-liquidity" import component_fees_fixed_hook_fee from "./pages/fees/fixed-hook-fee" import component_hooks_custom_curve from "./pages/hooks/custom-curve" +import component_hooks_msg_sender from "./pages/hooks/msg-sender" import component_hooks_no_op from "./pages/hooks/no-op" import component_initialize from "./pages/initialize" import component_swap from "./pages/swap" @@ -35,6 +36,10 @@ const routes: Route[] = [ path: "/hooks/custom-curve", component: component_hooks_custom_curve }, + { + path: "/hooks/msg-sender", + component: component_hooks_msg_sender + }, { path: "/hooks/no-op", component: component_hooks_no_op diff --git a/src/search.json b/src/search.json index 4cfb615e3..60df80a87 100644 --- a/src/search.json +++ b/src/search.json @@ -29,11 +29,13 @@ ], "hook": [ "/hooks/no-op", + "/hooks/msg-sender", "/hooks/custom-curve", "/fees/fixed-hook-fee" ], "hooks": [ "/hooks/no-op", + "/hooks/msg-sender", "/hooks/custom-curve", "/fees/fixed-hook-fee" ], @@ -51,6 +53,15 @@ "skip swap": [ "/hooks/no-op" ], + "msg.sender": [ + "/hooks/msg-sender" + ], + "msgsender": [ + "/hooks/msg-sender" + ], + "sender": [ + "/hooks/msg-sender" + ], "custom curve": [ "/hooks/custom-curve" ], From 5777527da4a73dfdc0cf15e9584f51dd4793c0b0 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 31 Jan 2024 13:05:07 -0500 Subject: [PATCH 4/6] updates to the latest v4-core --- forge-test/GetCurrentLockCaller.t.sol | 25 +++++++++++-------- forge-test/MsgSenderHookData.t.sol | 12 +++++---- .../hooks/msg-sender/GetCurrentLockCaller.sol | 6 +++-- .../hooks/msg-sender/MsgSenderHookData.sol | 6 +++-- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/forge-test/GetCurrentLockCaller.t.sol b/forge-test/GetCurrentLockCaller.t.sol index 62b428b2d..4026279f3 100644 --- a/forge-test/GetCurrentLockCaller.t.sol +++ b/forge-test/GetCurrentLockCaller.t.sol @@ -12,7 +12,7 @@ import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; import {Constants} from "v4-core/../test/utils/Constants.sol"; import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol"; -import {HookTest} from "./utils/HookTest.sol"; +import {HookTest} from "@v4-by-example/utils/HookTest.sol"; import {GetCurrentLockCaller} from "src/pages/hooks/msg-sender/GetCurrentLockCaller.sol"; import {HookMiner} from "./utils/HookMiner.sol"; @@ -43,11 +43,13 @@ contract GetCurrentLockCallerTest is HookTest { initializeRouter.initialize(poolKey, Constants.SQRT_RATIO_1_1, ZERO_BYTES); // Provide liquidity to the pool - modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-60, 60, 10 ether), ZERO_BYTES); - modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-120, 120, 10 ether), ZERO_BYTES); - modifyPositionRouter.modifyPosition( + modifyPositionRouter.modifyLiquidity(poolKey, IPoolManager.ModifyLiquidityParams(-60, 60, 10 ether), ZERO_BYTES); + modifyPositionRouter.modifyLiquidity( + poolKey, IPoolManager.ModifyLiquidityParams(-120, 120, 10 ether), ZERO_BYTES + ); + modifyPositionRouter.modifyLiquidity( poolKey, - IPoolManager.ModifyPositionParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), + IPoolManager.ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), ZERO_BYTES ); } @@ -77,16 +79,19 @@ contract GetCurrentLockCallerTest is HookTest { // Perform a test swap // int256 amount = 1e18; bool zeroForOne = true; - // vm.expectRevert(); + vm.expectRevert(); lockSwap(poolKey, amount, zeroForOne, ZERO_BYTES, true); // ------------------- // vm.stopPrank(); } - function lockSwap(PoolKey memory key, int256 amountSpecified, bool zeroForOne, bytes memory hookData, bool expectRevert) - internal - returns (BalanceDelta swapDelta) - { + function lockSwap( + PoolKey memory key, + int256 amountSpecified, + bool zeroForOne, + bytes memory hookData, + bool expectRevert + ) internal returns (BalanceDelta swapDelta) { IPoolManager.SwapParams memory params = IPoolManager.SwapParams({ zeroForOne: zeroForOne, amountSpecified: amountSpecified, diff --git a/forge-test/MsgSenderHookData.t.sol b/forge-test/MsgSenderHookData.t.sol index dcb2962d2..15883c272 100644 --- a/forge-test/MsgSenderHookData.t.sol +++ b/forge-test/MsgSenderHookData.t.sol @@ -11,7 +11,7 @@ import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; import {Constants} from "v4-core/../test/utils/Constants.sol"; import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol"; -import {HookTest} from "./utils/HookTest.sol"; +import {HookTest} from "@v4-by-example/utils/HookTest.sol"; import {MsgSenderHookData} from "src/pages/hooks/msg-sender/MsgSenderHookData.sol"; import {HookMiner} from "./utils/HookMiner.sol"; @@ -42,11 +42,13 @@ contract MsgSenderHookDataTest is HookTest { initializeRouter.initialize(poolKey, Constants.SQRT_RATIO_1_1, ZERO_BYTES); // Provide liquidity to the pool - modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-60, 60, 10 ether), ZERO_BYTES); - modifyPositionRouter.modifyPosition(poolKey, IPoolManager.ModifyPositionParams(-120, 120, 10 ether), ZERO_BYTES); - modifyPositionRouter.modifyPosition( + modifyPositionRouter.modifyLiquidity(poolKey, IPoolManager.ModifyLiquidityParams(-60, 60, 10 ether), ZERO_BYTES); + modifyPositionRouter.modifyLiquidity( + poolKey, IPoolManager.ModifyLiquidityParams(-120, 120, 10 ether), ZERO_BYTES + ); + modifyPositionRouter.modifyLiquidity( poolKey, - IPoolManager.ModifyPositionParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), + IPoolManager.ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), ZERO_BYTES ); } diff --git a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol index bfc7c3668..2d83d73c0 100644 --- a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol +++ b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol @@ -20,8 +20,10 @@ contract GetCurrentLockCaller is BaseHook { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, - beforeModifyPosition: false, - afterModifyPosition: false, + beforeAddLiquidity: false, + beforeRemoveLiquidity: false, + afterAddLiquidity: false, + afterRemoveLiquidity: false, beforeSwap: true, afterSwap: false, beforeDonate: false, diff --git a/src/pages/hooks/msg-sender/MsgSenderHookData.sol b/src/pages/hooks/msg-sender/MsgSenderHookData.sol index 1e30ed259..03271f4c5 100644 --- a/src/pages/hooks/msg-sender/MsgSenderHookData.sol +++ b/src/pages/hooks/msg-sender/MsgSenderHookData.sol @@ -21,8 +21,10 @@ contract MsgSenderHookData is BaseHook { return Hooks.Permissions({ beforeInitialize: false, afterInitialize: false, - beforeModifyPosition: false, - afterModifyPosition: false, + beforeAddLiquidity: false, + beforeRemoveLiquidity: false, + afterAddLiquidity: false, + afterRemoveLiquidity: false, beforeSwap: true, afterSwap: false, beforeDonate: false, From 809decdad1e3503c533f0c36ecd5c09e29db2776 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 31 Jan 2024 13:44:19 -0500 Subject: [PATCH 5/6] cleanup --- .../hooks/msg-sender/GetCurrentLockCaller.sol | 32 ++++---- .../hooks/msg-sender/MsgSenderHookData.sol | 32 ++++---- .../msg-sender/PoolManagerLock.solsnippet | 1 + src/pages/hooks/msg-sender/index.html.ts | 81 +++++++++++-------- src/pages/hooks/msg-sender/index.md | 16 +++- 5 files changed, 92 insertions(+), 70 deletions(-) diff --git a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol index 2d83d73c0..004f65ff3 100644 --- a/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol +++ b/src/pages/hooks/msg-sender/GetCurrentLockCaller.sol @@ -16,6 +16,22 @@ contract GetCurrentLockCaller is BaseHook { constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} + function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) + external + override + returns (bytes4) + { + // --- Read the user's address --- // + (, address user) = poolManager.getLock(1); + require(allowedUsers[user], "GetCurrentLockCaller: User not allowed"); + return BaseHook.beforeSwap.selector; + } + + // Helper function for demonstration + function setAllowedUser(address user, bool allowed) external { + allowedUsers[user] = allowed; + } + function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, @@ -32,20 +48,4 @@ contract GetCurrentLockCaller is BaseHook { accessLock: false }); } - - function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) - external - override - returns (bytes4) - { - // --- Read the user's address --- // - (, address user) = poolManager.getLock(1); - require(allowedUsers[user], "GetCurrentLockCaller: User not allowed"); - return BaseHook.beforeSwap.selector; - } - - // Helper function for demonstration - function setAllowedUser(address user, bool allowed) external { - allowedUsers[user] = allowed; - } } diff --git a/src/pages/hooks/msg-sender/MsgSenderHookData.sol b/src/pages/hooks/msg-sender/MsgSenderHookData.sol index 03271f4c5..a28371426 100644 --- a/src/pages/hooks/msg-sender/MsgSenderHookData.sol +++ b/src/pages/hooks/msg-sender/MsgSenderHookData.sol @@ -17,6 +17,22 @@ contract MsgSenderHookData is BaseHook { constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} + function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) + external + override + returns (bytes4) + { + // --- Read the user's address --- // + address user = abi.decode(hookData, (address)); + require(allowedUsers[user], "MsgSenderHookData: User not allowed"); + return BaseHook.beforeSwap.selector; + } + + // Helper function for demonstration + function setAllowedUser(address user, bool allowed) external { + allowedUsers[user] = allowed; + } + function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: false, @@ -33,20 +49,4 @@ contract MsgSenderHookData is BaseHook { accessLock: false }); } - - function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) - external - override - returns (bytes4) - { - // --- Read the user's address --- // - address user = abi.decode(hookData, (address)); - require(allowedUsers[user], "MsgSenderHookData: User not allowed"); - return BaseHook.beforeSwap.selector; - } - - // Helper function for demonstration - function setAllowedUser(address user, bool allowed) external { - allowedUsers[user] = allowed; - } } diff --git a/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet b/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet index e59e507b3..aa567c87f 100644 --- a/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet +++ b/src/pages/hooks/msg-sender/PoolManagerLock.solsnippet @@ -1,4 +1,5 @@ // User directly locks on the PoolManager +// - poolManager.getLock(1) returns alice's address vm.prank(alice); manager.lock( address(swapRouter), diff --git a/src/pages/hooks/msg-sender/index.html.ts b/src/pages/hooks/msg-sender/index.html.ts index d6e308476..f303506a5 100644 --- a/src/pages/hooks/msg-sender/index.html.ts +++ b/src/pages/hooks/msg-sender/index.html.ts @@ -14,15 +14,15 @@ export const keywords = [ export const codes = [ { fileName: "GetCurrentLockCaller.sol", - code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiQHY0LWJ5LWV4YW1wbGUvdXRpbHMvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7Y29uc29sZTJ9IGZyb20gImZvcmdlLXN0ZC9jb25zb2xlMi5zb2wiOwppbXBvcnQge0hvb2tzfSBmcm9tICJ2NC1jb3JlL2xpYnJhcmllcy9Ib29rcy5zb2wiOwppbXBvcnQge0lQb29sTWFuYWdlcn0gZnJvbSAidjQtY29yZS9pbnRlcmZhY2VzL0lQb29sTWFuYWdlci5zb2wiOwppbXBvcnQge1Bvb2xLZXl9IGZyb20gInY0LWNvcmUvdHlwZXMvUG9vbEtleS5zb2wiOwppbXBvcnQge0JhbGFuY2VEZWx0YX0gZnJvbSAidjQtY29yZS90eXBlcy9CYWxhbmNlRGVsdGEuc29sIjsKaW1wb3J0IHtMb2NrZXJzfSBmcm9tICJ2NC1jb3JlL2xpYnJhcmllcy9Mb2NrZXJzLnNvbCI7Cgpjb250cmFjdCBHZXRDdXJyZW50TG9ja0NhbGxlciBpcyBCYXNlSG9vayB7CiAgICBtYXBwaW5nKGFkZHJlc3MgdXNlciA9PiBib29sIGFsbG93ZWQpIHB1YmxpYyBhbGxvd2VkVXNlcnM7CgogICAgY29uc3RydWN0b3IoSVBvb2xNYW5hZ2VyIF9wb29sTWFuYWdlcikgQmFzZUhvb2soX3Bvb2xNYW5hZ2VyKSB7fQoKICAgIGZ1bmN0aW9uIGdldEhvb2tQZXJtaXNzaW9ucygpIHB1YmxpYyBwdXJlIG92ZXJyaWRlIHJldHVybnMgKEhvb2tzLlBlcm1pc3Npb25zIG1lbW9yeSkgewogICAgICAgIHJldHVybiBIb29rcy5QZXJtaXNzaW9ucyh7CiAgICAgICAgICAgIGJlZm9yZUluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckluaXRpYWxpemU6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVNb2RpZnlQb3NpdGlvbjogZmFsc2UsCiAgICAgICAgICAgIGFmdGVyTW9kaWZ5UG9zaXRpb246IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVTd2FwOiB0cnVlLAogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IGZhbHNlLAogICAgICAgICAgICBhY2Nlc3NMb2NrOiBmYWxzZQogICAgICAgIH0pOwogICAgfQoKICAgIGZ1bmN0aW9uIGJlZm9yZVN3YXAoYWRkcmVzcywgUG9vbEtleSBjYWxsZGF0YSBrZXksIElQb29sTWFuYWdlci5Td2FwUGFyYW1zIGNhbGxkYXRhLCBieXRlcyBjYWxsZGF0YSBob29rRGF0YSkKICAgICAgICBleHRlcm5hbAogICAgICAgIG92ZXJyaWRlCiAgICAgICAgcmV0dXJucyAoYnl0ZXM0KQogICAgewogICAgICAgIC8vIC0tLSBSZWFkIHRoZSB1c2VyJ3MgYWRkcmVzcyAtLS0gLy8KICAgICAgICAoLCBhZGRyZXNzIHVzZXIpID0gcG9vbE1hbmFnZXIuZ2V0TG9jaygxKTsKICAgICAgICByZXF1aXJlKGFsbG93ZWRVc2Vyc1t1c2VyXSwgIkdldEN1cnJlbnRMb2NrQ2FsbGVyOiBVc2VyIG5vdCBhbGxvd2VkIik7CiAgICAgICAgcmV0dXJuIEJhc2VIb29rLmJlZm9yZVN3YXAuc2VsZWN0b3I7CiAgICB9CgogICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBkZW1vbnN0cmF0aW9uCiAgICBmdW5jdGlvbiBzZXRBbGxvd2VkVXNlcihhZGRyZXNzIHVzZXIsIGJvb2wgYWxsb3dlZCkgZXh0ZXJuYWwgewogICAgICAgIGFsbG93ZWRVc2Vyc1t1c2VyXSA9IGFsbG93ZWQ7CiAgICB9Cn0K", + code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiQHY0LWJ5LWV4YW1wbGUvdXRpbHMvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7Y29uc29sZTJ9IGZyb20gImZvcmdlLXN0ZC9jb25zb2xlMi5zb2wiOwppbXBvcnQge0hvb2tzfSBmcm9tICJ2NC1jb3JlL2xpYnJhcmllcy9Ib29rcy5zb2wiOwppbXBvcnQge0lQb29sTWFuYWdlcn0gZnJvbSAidjQtY29yZS9pbnRlcmZhY2VzL0lQb29sTWFuYWdlci5zb2wiOwppbXBvcnQge1Bvb2xLZXl9IGZyb20gInY0LWNvcmUvdHlwZXMvUG9vbEtleS5zb2wiOwppbXBvcnQge0JhbGFuY2VEZWx0YX0gZnJvbSAidjQtY29yZS90eXBlcy9CYWxhbmNlRGVsdGEuc29sIjsKaW1wb3J0IHtMb2NrZXJzfSBmcm9tICJ2NC1jb3JlL2xpYnJhcmllcy9Mb2NrZXJzLnNvbCI7Cgpjb250cmFjdCBHZXRDdXJyZW50TG9ja0NhbGxlciBpcyBCYXNlSG9vayB7CiAgICBtYXBwaW5nKGFkZHJlc3MgdXNlciA9PiBib29sIGFsbG93ZWQpIHB1YmxpYyBhbGxvd2VkVXNlcnM7CgogICAgY29uc3RydWN0b3IoSVBvb2xNYW5hZ2VyIF9wb29sTWFuYWdlcikgQmFzZUhvb2soX3Bvb2xNYW5hZ2VyKSB7fQoKICAgIGZ1bmN0aW9uIGJlZm9yZVN3YXAoYWRkcmVzcywgUG9vbEtleSBjYWxsZGF0YSBrZXksIElQb29sTWFuYWdlci5Td2FwUGFyYW1zIGNhbGxkYXRhLCBieXRlcyBjYWxsZGF0YSBob29rRGF0YSkKICAgICAgICBleHRlcm5hbAogICAgICAgIG92ZXJyaWRlCiAgICAgICAgcmV0dXJucyAoYnl0ZXM0KQogICAgewogICAgICAgIC8vIC0tLSBSZWFkIHRoZSB1c2VyJ3MgYWRkcmVzcyAtLS0gLy8KICAgICAgICAoLCBhZGRyZXNzIHVzZXIpID0gcG9vbE1hbmFnZXIuZ2V0TG9jaygxKTsKICAgICAgICByZXF1aXJlKGFsbG93ZWRVc2Vyc1t1c2VyXSwgIkdldEN1cnJlbnRMb2NrQ2FsbGVyOiBVc2VyIG5vdCBhbGxvd2VkIik7CiAgICAgICAgcmV0dXJuIEJhc2VIb29rLmJlZm9yZVN3YXAuc2VsZWN0b3I7CiAgICB9CgogICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBkZW1vbnN0cmF0aW9uCiAgICBmdW5jdGlvbiBzZXRBbGxvd2VkVXNlcihhZGRyZXNzIHVzZXIsIGJvb2wgYWxsb3dlZCkgZXh0ZXJuYWwgewogICAgICAgIGFsbG93ZWRVc2Vyc1t1c2VyXSA9IGFsbG93ZWQ7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0SG9va1Blcm1pc3Npb25zKCkgcHVibGljIHB1cmUgb3ZlcnJpZGUgcmV0dXJucyAoSG9va3MuUGVybWlzc2lvbnMgbWVtb3J5KSB7CiAgICAgICAgcmV0dXJuIEhvb2tzLlBlcm1pc3Npb25zKHsKICAgICAgICAgICAgYmVmb3JlSW5pdGlhbGl6ZTogZmFsc2UsCiAgICAgICAgICAgIGFmdGVySW5pdGlhbGl6ZTogZmFsc2UsCiAgICAgICAgICAgIGJlZm9yZUFkZExpcXVpZGl0eTogZmFsc2UsCiAgICAgICAgICAgIGJlZm9yZVJlbW92ZUxpcXVpZGl0eTogZmFsc2UsCiAgICAgICAgICAgIGFmdGVyQWRkTGlxdWlkaXR5OiBmYWxzZSwKICAgICAgICAgICAgYWZ0ZXJSZW1vdmVMaXF1aWRpdHk6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVTd2FwOiB0cnVlLAogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IGZhbHNlLAogICAgICAgICAgICBhY2Nlc3NMb2NrOiBmYWxzZQogICAgICAgIH0pOwogICAgfQp9Cg==", }, { fileName: "MsgSenderHookData.sol", - code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiQHY0LWJ5LWV4YW1wbGUvdXRpbHMvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7SG9va3N9IGZyb20gInY0LWNvcmUvbGlicmFyaWVzL0hvb2tzLnNvbCI7CmltcG9ydCB7SVBvb2xNYW5hZ2VyfSBmcm9tICJ2NC1jb3JlL2ludGVyZmFjZXMvSVBvb2xNYW5hZ2VyLnNvbCI7CmltcG9ydCB7UG9vbEtleX0gZnJvbSAidjQtY29yZS90eXBlcy9Qb29sS2V5LnNvbCI7CmltcG9ydCB7UG9vbElkLCBQb29sSWRMaWJyYXJ5fSBmcm9tICJ2NC1jb3JlL3R5cGVzL1Bvb2xJZC5zb2wiOwppbXBvcnQge0JhbGFuY2VEZWx0YX0gZnJvbSAidjQtY29yZS90eXBlcy9CYWxhbmNlRGVsdGEuc29sIjsKCmNvbnRyYWN0IE1zZ1NlbmRlckhvb2tEYXRhIGlzIEJhc2VIb29rIHsKICAgIHVzaW5nIFBvb2xJZExpYnJhcnkgZm9yIFBvb2xLZXk7CgogICAgbWFwcGluZyhhZGRyZXNzIHVzZXIgPT4gYm9vbCBhbGxvd2VkKSBwdWJsaWMgYWxsb3dlZFVzZXJzOwoKICAgIGNvbnN0cnVjdG9yKElQb29sTWFuYWdlciBfcG9vbE1hbmFnZXIpIEJhc2VIb29rKF9wb29sTWFuYWdlcikge30KCiAgICBmdW5jdGlvbiBnZXRIb29rUGVybWlzc2lvbnMoKSBwdWJsaWMgcHVyZSBvdmVycmlkZSByZXR1cm5zIChIb29rcy5QZXJtaXNzaW9ucyBtZW1vcnkpIHsKICAgICAgICByZXR1cm4gSG9va3MuUGVybWlzc2lvbnMoewogICAgICAgICAgICBiZWZvcmVJbml0aWFsaXplOiBmYWxzZSwKICAgICAgICAgICAgYWZ0ZXJJbml0aWFsaXplOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlTW9kaWZ5UG9zaXRpb246IGZhbHNlLAogICAgICAgICAgICBhZnRlck1vZGlmeVBvc2l0aW9uOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlU3dhcDogdHJ1ZSwKICAgICAgICAgICAgYWZ0ZXJTd2FwOiBmYWxzZSwKICAgICAgICAgICAgYmVmb3JlRG9uYXRlOiBmYWxzZSwKICAgICAgICAgICAgYWZ0ZXJEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBub09wOiBmYWxzZSwKICAgICAgICAgICAgYWNjZXNzTG9jazogZmFsc2UKICAgICAgICB9KTsKICAgIH0KCiAgICBmdW5jdGlvbiBiZWZvcmVTd2FwKGFkZHJlc3MsIFBvb2xLZXkgY2FsbGRhdGEga2V5LCBJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSwgYnl0ZXMgY2FsbGRhdGEgaG9va0RhdGEpCiAgICAgICAgZXh0ZXJuYWwKICAgICAgICBvdmVycmlkZQogICAgICAgIHJldHVybnMgKGJ5dGVzNCkKICAgIHsKICAgICAgICAvLyAtLS0gUmVhZCB0aGUgdXNlcidzIGFkZHJlc3MgLS0tIC8vCiAgICAgICAgYWRkcmVzcyB1c2VyID0gYWJpLmRlY29kZShob29rRGF0YSwgKGFkZHJlc3MpKTsKICAgICAgICByZXF1aXJlKGFsbG93ZWRVc2Vyc1t1c2VyXSwgIk1zZ1NlbmRlckhvb2tEYXRhOiBVc2VyIG5vdCBhbGxvd2VkIik7CiAgICAgICAgcmV0dXJuIEJhc2VIb29rLmJlZm9yZVN3YXAuc2VsZWN0b3I7CiAgICB9CgogICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBkZW1vbnN0cmF0aW9uCiAgICBmdW5jdGlvbiBzZXRBbGxvd2VkVXNlcihhZGRyZXNzIHVzZXIsIGJvb2wgYWxsb3dlZCkgZXh0ZXJuYWwgewogICAgICAgIGFsbG93ZWRVc2Vyc1t1c2VyXSA9IGFsbG93ZWQ7CiAgICB9Cn0K", + code: "Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4xOTsKCi8vIFRPRE86IHVwZGF0ZSB0byB2NC1wZXJpcGhlcnkvQmFzZUhvb2suc29sIHdoZW4gaXRzIGNvbXBhdGlibGUKaW1wb3J0IHtCYXNlSG9va30gZnJvbSAiQHY0LWJ5LWV4YW1wbGUvdXRpbHMvQmFzZUhvb2suc29sIjsKCmltcG9ydCB7SG9va3N9IGZyb20gInY0LWNvcmUvbGlicmFyaWVzL0hvb2tzLnNvbCI7CmltcG9ydCB7SVBvb2xNYW5hZ2VyfSBmcm9tICJ2NC1jb3JlL2ludGVyZmFjZXMvSVBvb2xNYW5hZ2VyLnNvbCI7CmltcG9ydCB7UG9vbEtleX0gZnJvbSAidjQtY29yZS90eXBlcy9Qb29sS2V5LnNvbCI7CmltcG9ydCB7UG9vbElkLCBQb29sSWRMaWJyYXJ5fSBmcm9tICJ2NC1jb3JlL3R5cGVzL1Bvb2xJZC5zb2wiOwppbXBvcnQge0JhbGFuY2VEZWx0YX0gZnJvbSAidjQtY29yZS90eXBlcy9CYWxhbmNlRGVsdGEuc29sIjsKCmNvbnRyYWN0IE1zZ1NlbmRlckhvb2tEYXRhIGlzIEJhc2VIb29rIHsKICAgIHVzaW5nIFBvb2xJZExpYnJhcnkgZm9yIFBvb2xLZXk7CgogICAgbWFwcGluZyhhZGRyZXNzIHVzZXIgPT4gYm9vbCBhbGxvd2VkKSBwdWJsaWMgYWxsb3dlZFVzZXJzOwoKICAgIGNvbnN0cnVjdG9yKElQb29sTWFuYWdlciBfcG9vbE1hbmFnZXIpIEJhc2VIb29rKF9wb29sTWFuYWdlcikge30KCiAgICBmdW5jdGlvbiBiZWZvcmVTd2FwKGFkZHJlc3MsIFBvb2xLZXkgY2FsbGRhdGEga2V5LCBJUG9vbE1hbmFnZXIuU3dhcFBhcmFtcyBjYWxsZGF0YSwgYnl0ZXMgY2FsbGRhdGEgaG9va0RhdGEpCiAgICAgICAgZXh0ZXJuYWwKICAgICAgICBvdmVycmlkZQogICAgICAgIHJldHVybnMgKGJ5dGVzNCkKICAgIHsKICAgICAgICAvLyAtLS0gUmVhZCB0aGUgdXNlcidzIGFkZHJlc3MgLS0tIC8vCiAgICAgICAgYWRkcmVzcyB1c2VyID0gYWJpLmRlY29kZShob29rRGF0YSwgKGFkZHJlc3MpKTsKICAgICAgICByZXF1aXJlKGFsbG93ZWRVc2Vyc1t1c2VyXSwgIk1zZ1NlbmRlckhvb2tEYXRhOiBVc2VyIG5vdCBhbGxvd2VkIik7CiAgICAgICAgcmV0dXJuIEJhc2VIb29rLmJlZm9yZVN3YXAuc2VsZWN0b3I7CiAgICB9CgogICAgLy8gSGVscGVyIGZ1bmN0aW9uIGZvciBkZW1vbnN0cmF0aW9uCiAgICBmdW5jdGlvbiBzZXRBbGxvd2VkVXNlcihhZGRyZXNzIHVzZXIsIGJvb2wgYWxsb3dlZCkgZXh0ZXJuYWwgewogICAgICAgIGFsbG93ZWRVc2Vyc1t1c2VyXSA9IGFsbG93ZWQ7CiAgICB9CgogICAgZnVuY3Rpb24gZ2V0SG9va1Blcm1pc3Npb25zKCkgcHVibGljIHB1cmUgb3ZlcnJpZGUgcmV0dXJucyAoSG9va3MuUGVybWlzc2lvbnMgbWVtb3J5KSB7CiAgICAgICAgcmV0dXJuIEhvb2tzLlBlcm1pc3Npb25zKHsKICAgICAgICAgICAgYmVmb3JlSW5pdGlhbGl6ZTogZmFsc2UsCiAgICAgICAgICAgIGFmdGVySW5pdGlhbGl6ZTogZmFsc2UsCiAgICAgICAgICAgIGJlZm9yZUFkZExpcXVpZGl0eTogZmFsc2UsCiAgICAgICAgICAgIGJlZm9yZVJlbW92ZUxpcXVpZGl0eTogZmFsc2UsCiAgICAgICAgICAgIGFmdGVyQWRkTGlxdWlkaXR5OiBmYWxzZSwKICAgICAgICAgICAgYWZ0ZXJSZW1vdmVMaXF1aWRpdHk6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVTd2FwOiB0cnVlLAogICAgICAgICAgICBhZnRlclN3YXA6IGZhbHNlLAogICAgICAgICAgICBiZWZvcmVEb25hdGU6IGZhbHNlLAogICAgICAgICAgICBhZnRlckRvbmF0ZTogZmFsc2UsCiAgICAgICAgICAgIG5vT3A6IGZhbHNlLAogICAgICAgICAgICBhY2Nlc3NMb2NrOiBmYWxzZQogICAgICAgIH0pOwogICAgfQp9Cg==", }, { fileName: "PoolManagerLock.sol", - code: "Ly8gVXNlciBkaXJlY3RseSBsb2NrcyBvbiB0aGUgUG9vbE1hbmFnZXIKdm0ucHJhbmsoYWxpY2UpOwptYW5hZ2VyLmxvY2soCiAgICBhZGRyZXNzKHN3YXBSb3V0ZXIpLAogICAgYWJpLmVuY29kZShQb29sU3dhcFRlc3QuQ2FsbGJhY2tEYXRhKGFkZHJlc3ModGhpcyksIHRlc3RTZXR0aW5ncywga2V5LCBwYXJhbXMsIGhvb2tEYXRhKSkKKTs=", + code: "Ly8gVXNlciBkaXJlY3RseSBsb2NrcyBvbiB0aGUgUG9vbE1hbmFnZXIKLy8gLSBwb29sTWFuYWdlci5nZXRMb2NrKDEpIHJldHVybnMgYWxpY2UncyBhZGRyZXNzCnZtLnByYW5rKGFsaWNlKTsKbWFuYWdlci5sb2NrKAogICAgYWRkcmVzcyhzd2FwUm91dGVyKSwKICAgIGFiaS5lbmNvZGUoUG9vbFN3YXBUZXN0LkNhbGxiYWNrRGF0YShhZGRyZXNzKHRoaXMpLCB0ZXN0U2V0dGluZ3MsIGtleSwgcGFyYW1zLCBob29rRGF0YSkpCik7", }, { fileName: "PoolSwapTestHookData.sol", @@ -31,13 +31,19 @@ export const codes = [ ] const html = `

    Please note there are multiple solutions -- each includes different tradeoffs

    +

    Use-cases

    +

    Accessing the user's address inside a hook provides a lot of expressivity such as:

    +
      +
    • Volume-based discounts
    • +
    • Permissioned / compliant access
    • +

    Using hookData

    Callers (EOAs / contracts / multisigs) of periphery contracts (PoolSwapTest) can provide the user's address as the hookData argument

      -
    • Tradeoff: Routing, quoters, and user interfaces will need to be aware of this non-standard parameter
    • +
    • Tradeoff: Routing, quoters, and user interfaces will need to be aware of this non-standard parameter. hookData breaks generic/conventional paths

    Get Lock Caller

    -

    Callers can use the PoolManager to invoke a periphery router. The lock caller is saved in transient storage and can be accessed within a hook

    +

    Callers directly use PoolManager.lock to invoke a periphery router. The lock caller (i.e. EOA) is saved in transient storage and can be accessed within a hook

    • Tradeoff: the transaction entrypoint is poolManager.lock and not PoolSwapTest.swap, leading to unconventional UX
    @@ -71,21 +77,6 @@ swapRouter.swap(key, params, testSettings, hookData); 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: false - }); - } - function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData) external override @@ -101,10 +92,28 @@ swapRouter.swap(key, params, testSettings, hookData); function setAllowedUser(address user, bool allowed) external { allowedUsers[user] = allowed; } + + function getHookPermissions() public pure override returns (Hooks.Permissions memory) { + return Hooks.Permissions({ + beforeInitialize: false, + afterInitialize: false, + beforeAddLiquidity: false, + beforeRemoveLiquidity: false, + afterAddLiquidity: false, + afterRemoveLiquidity: false, + beforeSwap: true, + afterSwap: false, + beforeDonate: false, + afterDonate: false, + noOp: false, + accessLock: false + }); + } }

    Get Lock Caller

    Invoke poolManager.lock with the user, and specify the PoolSwapTest router

    // User directly locks on the PoolManager
    +// - poolManager.getLock(1) returns alice's address
     vm.prank(alice);
     manager.lock(
         address(swapRouter),
    @@ -129,21 +138,6 @@ manager.lock(
     
         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: false
    -        });
    -    }
    -
         function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata hookData)
             external
             override
    @@ -159,6 +153,23 @@ manager.lock(
         function setAllowedUser(address user, bool allowed) external {
             allowedUsers[user] = allowed;
         }
    +
    +    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
    +        return Hooks.Permissions({
    +            beforeInitialize: false,
    +            afterInitialize: false,
    +            beforeAddLiquidity: false,
    +            beforeRemoveLiquidity: false,
    +            afterAddLiquidity: false,
    +            afterRemoveLiquidity: false,
    +            beforeSwap: true,
    +            afterSwap: false,
    +            beforeDonate: false,
    +            afterDonate: false,
    +            noOp: false,
    +            accessLock: false
    +        });
    +    }
     }
     
    ` diff --git a/src/pages/hooks/msg-sender/index.md b/src/pages/hooks/msg-sender/index.md index 47351fa94..d72a7ebc8 100644 --- a/src/pages/hooks/msg-sender/index.md +++ b/src/pages/hooks/msg-sender/index.md @@ -7,28 +7,37 @@ keywords: [hook, hooks, msg.sender, msgsender, sender] Please note there are multiple solutions -- each includes different tradeoffs +### Use-cases + +Accessing the user's address inside a hook provides a lot of expressivity such as: + +- Volume-based discounts +- Permissioned / compliant access + ### Using `hookData` Callers (EOAs / contracts / multisigs) of periphery contracts (`PoolSwapTest`) can provide the user's address as the `hookData` argument -* Tradeoff: Routing, quoters, and user interfaces will need to be aware of this non-standard parameter +- Tradeoff: Routing, quoters, and user interfaces will need to be aware of this non-standard parameter. `hookData` breaks generic/conventional paths ### Get Lock Caller -Callers can use the `PoolManager` to invoke a periphery router. The lock caller is saved in transient storage and can be accessed within a hook +Callers directly use `PoolManager.lock` to invoke a periphery router. The lock caller (i.e. EOA) is saved in transient storage and can be accessed within a hook -* Tradeoff: the transaction entrypoint is `poolManager.lock` and not `PoolSwapTest.swap`, leading to unconventional UX +- Tradeoff: the transaction entrypoint is `poolManager.lock` and not `PoolSwapTest.swap`, leading to unconventional UX --- ## `bytes memory hookData` Provide the user's address to the `PoolSwapTest` + ```solidity {{{PoolSwapTestHookData}}} ``` Decode the `hookData` into an `address` type + ```solidity {{{MsgSenderHookData}}} ``` @@ -36,6 +45,7 @@ Decode the `hookData` into an `address` type ## Get Lock Caller Invoke `poolManager.lock` with the user, and specify the `PoolSwapTest` router + ```solidity {{{PoolManagerLock}}} ``` From c31107926768005f69e746c3dda9e4dc96495695 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 31 Jan 2024 13:45:02 -0500 Subject: [PATCH 6/6] changelog --- src/pages/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 2bdaac718..d7916d1f0 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -8,7 +8,7 @@ import styles from "./index.module.css" import youTube from "../components/youtube.png" import { ROUTES, ROUTES_BY_CATEGORY, TRANSLATIONS } from "../nav" -const UPDATES = ["2024/01/23 - Dynamic Fees", "2024/01/08 - Quoter", "2023/12/15 - Update v4", "2023/12/11 - Custom Curves", "2023/12/03 - Static Hook Fee", "2023/11/28 - Updated pool initialization", "2023/11/28 - NoOp", "2023/11/13 - Make snippets concise", "2023/10/18 - Initial V4 Snippets"] +const UPDATES = ["2024/01/31 - msg.sender", "2024/01/23 - Dynamic Fees", "2024/01/08 - Quoter", "2023/12/15 - Update v4", "2023/12/11 - Custom Curves", "2023/12/03 - Static Hook Fee", "2023/11/28 - Updated pool initialization", "2023/11/28 - NoOp", "2023/11/13 - Make snippets concise", "2023/10/18 - Initial V4 Snippets"] export default function HomePage() { const [query, setQuery] = useState("")