Skip to content

Commit

Permalink
Merge pull request #21 from uniswapfoundation/msg.sender
Browse files Browse the repository at this point in the history
Access msg.sender
  • Loading branch information
saucepoint authored Jan 31, 2024
2 parents 57dd289 + c311079 commit d344f3f
Show file tree
Hide file tree
Showing 14 changed files with 613 additions and 1 deletion.
120 changes: 120 additions & 0 deletions forge-test/GetCurrentLockCaller.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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 "@v4-by-example/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.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.ModifyLiquidityParams(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));
}
}
86 changes: 86 additions & 0 deletions forge-test/MsgSenderHookData.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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 "@v4-by-example/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.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.ModifyLiquidityParams(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();
}
}
7 changes: 7 additions & 0 deletions src/keywords.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
"swap",
"skip swap"
],
"/hooks/msg-sender": [
"hook",
"hooks",
"msg.sender",
"msgsender",
"sender"
],
"/hooks/custom-curve": [
"hook",
"hooks",
Expand Down
4 changes: 4 additions & 0 deletions src/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export const HOOK_ROUTES: Route[] = [
{
path: "custom-curve",
title: "Custom Curve"
},
{
path: "msg-sender",
title: "Access msg.sender"
}
]

Expand Down
51 changes: 51 additions & 0 deletions src/pages/hooks/msg-sender/GetCurrentLockCaller.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 {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 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,
afterInitialize: false,
beforeAddLiquidity: false,
beforeRemoveLiquidity: false,
afterAddLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
noOp: false,
accessLock: false
});
}
}
52 changes: 52 additions & 0 deletions src/pages/hooks/msg-sender/MsgSenderHookData.sol
Original file line number Diff line number Diff line change
@@ -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 {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 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,
afterInitialize: false,
beforeAddLiquidity: false,
beforeRemoveLiquidity: false,
afterAddLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: false,
afterDonate: false,
noOp: false,
accessLock: false
});
}
}
7 changes: 7 additions & 0 deletions src/pages/hooks/msg-sender/PoolManagerLock.solsnippet
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// User directly locks on the PoolManager
// - poolManager.getLock(1) returns alice's address
vm.prank(alice);
manager.lock(
address(swapRouter),
abi.encode(PoolSwapTest.CallbackData(address(this), testSettings, key, params, hookData))
);
7 changes: 7 additions & 0 deletions src/pages/hooks/msg-sender/PoolSwapTestHookData.solsnippet
Original file line number Diff line number Diff line change
@@ -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);
Loading

0 comments on commit d344f3f

Please sign in to comment.