Skip to content

Commit c712ec5

Browse files
committed
deploy: SentinelHookV1 live on Unichain Sepolia + fee-curve demo
- hook: 0x290d2d0af6dd11b6e235eac6d7528f5474753080 (CREATE2, 0x...3080 permission suffix, tx 0xcaeb7fb3…442029) - V1 demo pool created on STA/STB with liquidity - demo choreography on-chain: calm swap pays 5bps base; sustained trend ramps the fee continuously 500 -> 7704 (rate-limited steps, FeeUpdated events); 2h quiet decays back at -500/step toward base - BaseScript: hook binding now env-overridable (SENTINEL_HOOK_ADDRESS), default unchanged (V0) - scripts: 05_DeployHookV1.s.sol, 06_DemoSwapsV1.s.sol
1 parent bdff599 commit c712ec5

9 files changed

Lines changed: 21410 additions & 128 deletions

File tree

broadcast/01_CreatePoolAndAddLiquidity.s.sol/1301/run-1787525657827.json

Lines changed: 398 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/01_CreatePoolAndAddLiquidity.s.sol/1301/run-latest.json

Lines changed: 127 additions & 127 deletions
Large diffs are not rendered by default.

broadcast/05_DeployHookV1.s.sol/1301/run-1787525429812.json

Lines changed: 57 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/05_DeployHookV1.s.sol/1301/run-latest.json

Lines changed: 57 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/06_DemoSwapsV1.s.sol/1301/run-1787526012626.json

Lines changed: 10318 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/06_DemoSwapsV1.s.sol/1301/run-latest.json

Lines changed: 10318 additions & 0 deletions
Large diffs are not rendered by default.

script/05_DeployHookV1.s.sol

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
5+
import {HookMiner} from "@uniswap/v4-periphery/src/utils/HookMiner.sol";
6+
7+
import {BaseScript} from "./base/BaseScript.sol";
8+
import {console2} from "forge-std/console2.sol";
9+
10+
import {SentinelHookV1} from "../src/SentinelHookV1.sol";
11+
12+
/// @notice Mines the address and deploys the SentinelHookV1.sol Hook contract
13+
/// (continuous vol-EMA toxicity pricing). Same permission bits as V0:
14+
/// beforeInitialize | afterInitialize | beforeSwap.
15+
contract DeployHookV1Script is BaseScript {
16+
function run() public {
17+
// hook contracts must have specific flags encoded in the address
18+
uint160 flags =
19+
uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG);
20+
21+
// Mine a salt that will produce a hook address with the correct flags
22+
bytes memory constructorArgs = abi.encode(poolManager);
23+
(address hookAddress, bytes32 salt) =
24+
HookMiner.find(CREATE2_FACTORY, flags, type(SentinelHookV1).creationCode, constructorArgs);
25+
26+
// Deploy the hook using CREATE2
27+
vm.startBroadcast();
28+
SentinelHookV1 sentinel = new SentinelHookV1{salt: salt}(poolManager);
29+
vm.stopBroadcast();
30+
31+
require(address(sentinel) == hookAddress, "DeployHookV1Script: Hook Address Mismatch");
32+
33+
console2.log("SentinelHookV1 deployed:", address(sentinel));
34+
console2.log("salt:", vm.toString(salt));
35+
}
36+
}

script/06_DemoSwapsV1.s.sol

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.26;
3+
4+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
5+
import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
6+
import {LPFeeLibrary} from "@uniswap/v4-core/src/libraries/LPFeeLibrary.sol";
7+
import {console2} from "forge-std/console2.sol";
8+
9+
import {BaseScript} from "./base/BaseScript.sol";
10+
import {SentinelHookV1} from "../src/SentinelHookV1.sol";
11+
12+
/// @notice Demo choreography for the live SentinelHookV1 pool:
13+
/// 1. a calm swap — fee stays at the 0.05% base,
14+
/// 2. a sustained one-directional trend — realized volatility over the
15+
/// 60-second lookback feeds the EMA and the fee ramps toward the 1.00%
16+
/// cap, rate-limited to 0.05% per update, one FeeUpdated event per step.
17+
/// 3. quiet — the EMA decays and the fee eases back toward base.
18+
contract DemoSwapsV1Script is BaseScript {
19+
using PoolIdLibrary for PoolKey;
20+
21+
function run() external {
22+
PoolKey memory poolKey = PoolKey({
23+
currency0: currency0,
24+
currency1: currency1,
25+
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG,
26+
tickSpacing: 60,
27+
hooks: hookContract
28+
});
29+
bytes memory hookData = new bytes(0);
30+
SentinelHookV1 hook = SentinelHookV1(address(hookContract));
31+
PoolId poolId = poolKey.toId();
32+
33+
vm.startBroadcast();
34+
35+
token0.approve(address(swapRouter), type(uint256).max);
36+
token1.approve(address(swapRouter), type(uint256).max);
37+
38+
// 1. Calm swap — pays the base fee.
39+
swapRouter.swapExactTokensForTokens({
40+
amountIn: 0.02e18,
41+
amountOutMin: 0,
42+
zeroForOne: true,
43+
poolKey: poolKey,
44+
hookData: hookData,
45+
receiver: deployerAddress,
46+
deadline: block.timestamp + 300
47+
});
48+
console2.log("1) calm swap fee:", hook.getCurrentFee(poolId));
49+
50+
// 2. Sustained trend: repeated same-direction swaps. Each pays the
51+
// fee set BEFORE it executes; the EMA feeds on realized moves.
52+
uint24 prev = hook.getCurrentFee(poolId);
53+
for (uint256 i = 0; i < 45; i++) {
54+
vm.warp(block.timestamp + 3);
55+
swapRouter.swapExactTokensForTokens({
56+
amountIn: 0.1e18,
57+
amountOutMin: 0,
58+
zeroForOne: false,
59+
poolKey: poolKey,
60+
hookData: hookData,
61+
receiver: deployerAddress,
62+
deadline: block.timestamp + 300
63+
});
64+
uint24 fee = hook.getCurrentFee(poolId);
65+
if (fee != prev) {
66+
console2.log("2) ramp step:", fee);
67+
prev = fee;
68+
}
69+
}
70+
require(prev > hook.BASE_FEE(), "demo: fee did not ramp");
71+
require(prev <= hook.CAP_FEE(), "demo: fee above cap");
72+
73+
// 3. Quiet period: EMA decays; fee steps back down.
74+
vm.warp(block.timestamp + 2 hours);
75+
for (uint256 i = 0; i < 45; i++) {
76+
vm.warp(block.timestamp + 3);
77+
swapRouter.swapExactTokensForTokens({
78+
amountIn: 0.001e18,
79+
amountOutMin: 0,
80+
zeroForOne: true,
81+
poolKey: poolKey,
82+
hookData: hookData,
83+
receiver: deployerAddress,
84+
deadline: block.timestamp + 300
85+
});
86+
uint24 fee = hook.getCurrentFee(poolId);
87+
if (fee != prev) {
88+
console2.log("3) decay step:", fee);
89+
prev = fee;
90+
}
91+
}
92+
console2.log("final fee:", prev);
93+
console2.log("ema (wad):", hook.getEmaRateWad(poolId));
94+
95+
vm.stopBroadcast();
96+
}
97+
}

script/base/BaseScript.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ contract BaseScript is Script, Deployers {
2424
/////////////////////////////////////
2525
IERC20 internal constant token0 = IERC20(0x345A187ace5808B0F7030d82cB2b444AcDa8Af1C); // Sentinel Test Alpha (Unichain Sepolia)
2626
IERC20 internal constant token1 = IERC20(0x52611F5C1e35E3213E5155483311A2C9Ab310138); // Sentinel Test Beta (Unichain Sepolia)
27-
IHooks constant hookContract = IHooks(0xCbD5bAc7B96770d7f18b97D05d6518A4D0913080); // SentinelHookV0 (Unichain Sepolia)
27+
IHooks immutable hookContract; // SentinelHookV0 by default; set SENTINEL_HOOK_ADDRESS to bind another (e.g. V1)
2828
/////////////////////////////////////
2929

3030
Currency immutable currency0;
@@ -46,6 +46,7 @@ contract BaseScript is Script, Deployers {
4646
vm.label(address(token0), "Currency0");
4747
vm.label(address(token1), "Currency1");
4848

49+
hookContract = IHooks(vm.envOr("SENTINEL_HOOK_ADDRESS", 0xCbD5bAc7B96770d7f18b97D05d6518A4D0913080));
4950
vm.label(address(hookContract), "HookContract");
5051
}
5152

0 commit comments

Comments
 (0)