A Uniswap v4 hook that protects LPs from toxic arbitrage by charging asymmetric fees based on whether swaps move the pool price toward or away from the Chainlink oracle price.
- Get Oracle Price — Fetch current price from Chainlink (ETH/USD, LINK/USD)
- Get Pool Price — Derive from current
sqrtPriceX96 - Calculate Deviation —
|poolPrice - oraclePrice| / oraclePricein basis points - Classify Zone — TIGHT (0-1%), NORMAL (1-3%), ELEVATED (3-5%), HIGH (5-10%), EXTREME (>10%)
- Determine Direction — Does swap move price TOWARD or AWAY from oracle?
- Apply Asymmetric Fee — Lower fee for stabilizing trades, higher for arbitrage
| Zone | Toward Oracle | Away from Oracle |
|---|---|---|
| TIGHT (0-1%) | 5 | 10 |
| NORMAL (1-3%) | 10 | 30 |
| ELEVATED (3-5%) | 20 | 50 |
| HIGH (5-10%) | 30 | 100 |
| EXTREME (>10%) | 50 | 200 |
Max fee cap: 200 bps (2%)
src/
├── DynamicFee.sol # Main hook — beforeSwap returns dynamic fee
├── base/
│ └── BaseHook.sol # Minimal base hook with permission validation
└── libraries/
├── OracleManager.sol # Chainlink integration (staleness checks, decimal normalization)
├── DeviationMonitor.sol # Deviation calculation and zone classification
└── FeeCalculator.sol # Fee matrix lookup
| Hook | Enabled | Purpose |
|---|---|---|
beforeSwap |
Yes | Calculate and return dynamic fee with OVERRIDE_FEE_FLAG |
afterSwap |
Yes | Update zone tracking, emit ZoneTransition events |
forge install
forge build
forge test -vvv
forge coverage| Contract | Address | BaseScan |
|---|---|---|
| DynamicFee Hook | 0x9788B8495ebcEC1C1D1436681B0F56C6fc0140c0 | view |
| tWETH (Mock) | 0x839Cc782708f1768F0F7591eA0c7D08290ba2a3c | view |
| tUSDC (Mock) | 0x8b6de320b93c2f8dEE5C9392A001E03CE6cc8Fe6 | view |
| tLINK (Mock) | 0x16538c37818d580F7f919D4583D7935C8624567E | view |
| PoolModifyLiquidityTest | 0x9f12E9d064398e07153Ca7E1401C71343edB772B | view |
| PoolSwapTest | 0xF778eF19F4A0065430C55a7cD09d287368947C29 | view |
| Uniswap v4 PoolManager | 0x05E73354cFDd6745C338b50BcFDfA3Aa6fA03408 | view |
| Contract | Address | Uniscan |
|---|---|---|
| DynamicFee Hook | 0xa5eCBF949D964760f3F7805f59eb4AAc1f2500c0 | view |
| Uniswap v4 PoolManager | 0x00B036B58a818B1BC34d502D3fE730Db729e62AC | view |
Deploy transaction: 0xcc6aca…
Arc is Circle's USDC-native L1 (USDC is the gas token). Uniswap v4 is not
pre-deployed on Arc testnet, so script/DeployArc.s.sol
stands up the full v4 stack (PoolManager + test routers), deploys freely-mintable
mock USDC (6 dp) and mock cirBTC (8 dp), deploys the DynamicFee hook to a
CREATE2 address carrying the beforeSwap/afterSwap flags, then creates and
seeds a single dynamic-fee USDC/cirBTC pool and runs warm-up swaps.
| Contract | Address | ArcScan |
|---|---|---|
| DynamicFee Hook | 0xA1Be807481F532c074380FCcF05be5e2A3ec80C0 | view |
| USDC (mock, 6dp) | 0xFE3f00877d20Fb599351182EAef78DE3EF531dF6 | view |
| cirBTC (mock, 8dp) | 0xAeE5a58b0ae058bfd358CeeB72e4804C16d94F5E | view |
| Uniswap v4 PoolManager | 0x7eA87A5919C119DC95855A0BE227fd3241c998F0 | view |
| PoolModifyLiquidityTest | 0xdD225f3B7b621287657B490B3bC945E3ecfC8EbA | view |
| PoolSwapTest | 0xAa096011E6604df33762d611cbBdaA0671F19Bdb | view |
Hook deploy (CREATE2): 0x5f22bd… · Deployer: 0xceeD79dBB39bA3C6Cddb57eb6343BE25FfD6dd56 · Pool: USDC/cirBTC (dynamic fee, tickSpacing 60)
- Chain: Arc Testnet (Chain ID
5042002) - RPC:
https://rpc.testnet.arc.network - Explorer: https://testnet.arcscan.app
- Faucet (USDC for gas): https://faucet.circle.com
- Canonical USDC:
0x3600000000000000000000000000000000000000(6 dp)
# Fund the deployer with testnet USDC (gas) from https://faucet.circle.com
export ARC_TESTNET_RPC_URL=https://rpc.testnet.arc.network
forge script script/DeployArc.s.sol:DeployArc \
--rpc-url arc_testnet --broadcast -vv| Action | Transaction |
|---|---|
| Deploy tWETH | 0x0eeded… |
| Deploy tUSDC | 0xf7451c… |
| Deploy tLINK | 0x7ca07c… |
| Deploy DynamicFee Hook (CREATE2) | 0x73dbaf… |
| Deploy LiquidityRouter | 0x6dd591… |
| Deploy SwapRouter | 0xa8cf52… |
| Action | Transaction |
|---|---|
| Configure ETH/USDC pool | 0xfa7cad… |
| Initialize ETH/USDC pool | 0x16d5c1… |
| Add liquidity ETH/USDC | 0x4a1bfa… |
| Configure LINK/USDC pool | 0xaea5ce… |
| Initialize LINK/USDC pool | 0x791a48… |
| Add liquidity LINK/USDC | 0xc37d54… |
| Configure ETH/LINK pool | 0xa369f3… |
| Initialize ETH/LINK pool | 0x7cb552… |
| Add liquidity ETH/LINK | 0x4bd929… |
Test swaps have not yet been re-executed against the new hook deployment. Run script/TestSwap.s.sol against the new pools to populate this section.
Arbitrage Against Oracle (High Fee)
- Oracle: ETH = $3000, Pool: ETH = $2900 (underpriced)
- Swap: Buy ETH (moves price away from oracle)
- Fee: 100+ bps (HIGH zone, AWAY direction)
Stabilizing Trade (Low Fee)
- Oracle: ETH = $3000, Pool: ETH = $3100 (overpriced)
- Swap: Sell ETH (moves price toward oracle)
- Fee: 10-30 bps (incentivizing rebalance)
- Chain: Base Sepolia (Chain ID 84532)
- PoolManager:
0x05E73354cFDd6745C338b50BcFDfA3Aa6fA03408 - Deployer:
0xbaacDCFfA93B984C914014F83Ee28B68dF88DC87
MIT