demo video · pitch video · App · Documentation · Before KNOT · Architecture · Verification · Pitch deck · On-chain proof · Security boundaries
UHI10 Hookathon · Sustainable Liquidity & MEV Protection
Project ID: HK-UHI10-1087
Partner: Unichain · Topics: Inter-connected pools · MEV · LVR · Custom hooks · CPMM · CFMM · LP liquidity · Unichain · Security
When the same token pair is split across several pools, one shallow or skewed pool can quote far beyond the price implied by the participating liquidity as a whole. KNOT gives those pools one shared, deterministic price boundary without pooling their custody or LP ownership.
KNOT is unaudited hackathon software. Its experiments measure a bounded quote rule in constructed scenarios; they do not prove universal MEV protection or realised LP profit.
Unichain. The release runs on Unichain Sepolia (chain 1301) rather than a private fork.
Every contract is deployed there, against the canonical Uniswap v4
PoolManager
at 0x00b036b5…729e62ac, and swaps settle through the
Universal Router
at 0xf70536B3…6Be5D with Permit2, so the write path is Uniswap's own rather than a bespoke one.
The federation, both hooks and both demo currencies are exact creation- and runtime-bytecode matches on Sourcify, the quote inspector reads live state from the public Unichain Sepolia RPC, and every transaction in See it on-chain is a Unichain Sepolia transaction anyone can open in the explorer.
The pools are live and anyone can trade against them.
- Open the app and connect a wallet on Unichain Sepolia.
- Claim 100 kETH and 100 kUSD from the demo faucet. Gas is paid in Unichain Sepolia ETH, which public faucets supply.
- Approve the token, then approve the router through Permit2. Both are one-time.
- Swap. The quote on screen is the limit the swap is submitted with, so it settles at the enforced bound or it reverts.
Choose the shallow pool to watch the boundary bind, or the deep pool to watch it stay inert because that pool's own curve is already the more conservative of the two.
Every swap below ran through the deployed hook on Unichain Sepolia against the canonical Uniswap v4 PoolManager. Click a hash to open it in the explorer.
| Swap through the hook | Transaction |
|---|---|
| Exact input · token0 → token1 | 0xe5c64d7c…c50df5 |
| Exact input · token1 → token0 | 0xffe5985a…c26ac3 |
| Exact output · token0 → token1 | 0xf712456b…3c3890 |
| Exact output · token1 → token0 | 0xa8a97a2d…253d93 |
| Contract | Address |
|---|---|
| KnotFederation | 0x49579383965f68079FB671b1d7AF0071cf206199 |
| KnotHook · deep | 0x55c73752E38403DDd30d03039568A5090256aa88 |
| KnotHook · shallow | 0x1c828fA6d4232E80aaeCEb143736092b0F822A88 |
| kETH · currency0 | 0x0784b9D734f2a6d13209087964640B1aD7699AAe |
| kUSD · currency1 | 0x243B3f2672Bdd36b63cA960AE201ECDDA4a7b83e |
The demo currencies minted their whole supply to the deployer, so a fresh wallet holds nothing
to swap with. KnotFaucet.sol fixes that: it
holds a funded balance of both currencies and pays a fixed drip per address behind a cooldown.
It never mints, so its exposure is capped by its funding, and it reverts loudly when drained.
It sits outside the hook trust boundary.
Status: deployed at
0xd9BdcF978669eCa41A6Fd44771AF95C7327f379b,
funded with 100,000 of each currency for 1,000 claims of 100 each behind an 8-hour
cooldown. Deployment:
0x864ab92d…b72f.
The app's claim button reads the recorded faucet entry in
deployments/unichain-sepolia.json; the manifest validator and the contracts page both
understand that field.
To deploy it from the wallet holding the demo currencies:
cd packages/contracts
CURRENCY0=0x0784b9D734f2a6d13209087964640B1aD7699AAe \
CURRENCY1=0x243B3f2672Bdd36b63cA960AE201ECDDA4a7b83e \
PRIVATE_KEY=<deployer-key> \
forge script script/deploy/DeployFaucet.s.sol:DeployFaucet \
--rpc-url https://sepolia.unichain.org --broadcastThen record the logged KNOT_FAUCET address in the manifest. Defaults pay 100 of each
currency per claim with an 8-hour cooldown per address.
Deployment and setup transactions
| Step | Transaction |
|---|---|
| Federation deployed | 0x3e69ab06…d5a91c |
| Deep hook deployed | 0xc22a5c0e…ce9439 |
| Shallow hook deployed | 0x176b66cd…516e1a |
| Deep member registered | 0xa423e8fb…19f024 |
| Shallow member registered | 0x2a456f86…22188d |
| Deep pool initialized | 0xefdd72e8…ff2c2c |
| Shallow pool initialized | 0xb9f3601e…27102e |
| Deep liquidity queued | 0x2de56b1d…fff904 |
| Shallow liquidity queued | 0x38a7c3cc…94f918 |
| Deep liquidity activated | 0xe384ac6f…95aec2 |
| Shallow liquidity activated | 0xad3ac152…4bd8d6 |
| kETH deployed | 0x7dc6666c…60b52b |
| kUSD deployed | 0x5627a491…062b2b |
Every participating pool keeps its own reserves, LP shares and fees. A shared federation stores an authenticated reserve book for the same token pair and maintains the sum of all member reserves in constant time.
Before each swap, KNOT computes two constant-product quotes:
- the quote from that pool's local reserves;
- the quote from the federation's aggregate reserves.
The trader receives the less favourable result:
If the local pool already offers the less favourable price, KNOT changes nothing. If the local pool offers the better price, the difference is never paid out and remains in that pool's reserves. No oracle, keeper, auction or off-chain classifier participates in the decision.
The canonical test fixture has two member pools:
| Member | token0 | token1 | Purpose |
|---|---|---|---|
| Deep | 1,000 | 1,000 | Balanced control |
| Shallow | 100 | 400 | Deliberately skewed member |
| Aggregate | 1,100 | 1,400 | Virtual federation curve |
For a 5-token exact-input trade through the shallow member:
| Quote | Output |
|---|---|
| Shallow pool alone | 18.993189 |
| Federation aggregate | 6.315922 |
| KNOT-enforced output | 6.315922 |
The bound reduces the isolated quote by 6,674 bps in this fixture. In the balanced deep member, the local quote is already more conservative and the bound is inert. The example proves the selection rule, not that reserve divergence is inherently toxic.
A router-only rule can be bypassed by using another router. KNOT enforces the boundary inside each participating pool:
| v4 primitive | KNOT use |
|---|---|
| Hook permissions | Intercept initialization, swaps and liquidity modification |
| Before-swap return delta | Replace the pool's normal quote with the bounded custom-accounting result |
| PoolManager unlock accounting | Settle input and output atomically |
| ERC-6909 claims | Back active reserves, pending deposits and refunds inside PoolManager custody |
| Dynamic fee flag | Keep the constant-product fee inside KNOT's quote instead of charging twice |
The hook returns no external oracle price. It executes a deterministic reserve policy against the same call that updates both the local and aggregate books.
flowchart LR
T[Trader] -->|swap| PM[Uniswap v4 PoolManager]
PM -->|beforeSwap| HA[KnotHook A]
PM -->|beforeSwap| HB[KnotHook B]
HA -->|authenticated update| F[KnotFederation]
HB -->|authenticated update| F
F -->|local + aggregate quote| HA
F -->|local + aggregate quote| HB
HA <-->|claims and settlement| PM
HB <-->|claims and settlement| PM
KnotHook.solowns one member's custom-accounting reserves, LP shares, pending liquidity and PoolManager claims.KnotFederation.solauthenticates members, maintains per-member books and updates the O(1) aggregate.KnotMath.solimplements fee-aware exact-input and exact-output quotes with rounding against the taker.
The federation never custodies tokens. It is the shared accounting authority; each hook remains the custody and LP boundary for its own pool.
New capital cannot enter the quote and immediately leave:
- Queue: the hook takes the assets but excludes them from active reserves.
- Mature: the request waits an immutable number of blocks.
- Activate: shares mint at the current reserve ratio, not the earlier deposit ratio.
- Exit lock: newly activated shares wait through a second maturity window before transfer or withdrawal.
- Cancel or claim: only the provider can cancel a pending request or claim unused assets.
Pending capital never blocks swaps, active withdrawals, other deposits or removal of an empty member. The custody identity is:
PoolManager claims = active reserves + inactive provider assets + provider refunds
KNOT is live on Unichain Sepolia (chain 1301) against the canonical Uniswap v4
PoolManager.
The release began at block 61583973; its two members were activated after the configured
one-block maturity window and then exercised through the deployed Universal Router in all four
single-hop modes.
| Component | Address |
|---|---|
| KnotFederation | 0x49579383965f68079FB671b1d7AF0071cf206199 |
| KnotHook · deep | 0x55c73752E38403DDd30d03039568A5090256aa88 |
| KnotHook · shallow | 0x1c828fA6d4232E80aaeCEb143736092b0F822A88 |
| kETH · currency0 | 0x0784b9D734f2a6d13209087964640B1aD7699AAe |
| kUSD · currency1 | 0x243B3f2672Bdd36b63cA960AE201ECDDA4a7b83e |
| Pool | Pool ID |
|---|---|
| Deep | 0xa37acc9c7b38bddba8dd58392ef174e031308d7d674250d3ac96ca5b668b6ce4 |
| Shallow | 0xfd1361ff40dcbffa9fbf20ab5dc741b0378f74cfb55c0d9519322126fe8b6d71 |
The public proof transactions are linked in the canonical
unichain-sepolia.json manifest. That manifest also records
the runtime code hash, post-proof reserve snapshot, quote outputs, PoolManager claim backing, and
each deployment, registration, initialization, activation, and router transaction hash. The web
app refuses to build an active release if any required proof field is missing or inconsistent.
The federation, both hooks, and both test tokens are exact creation- and runtime-bytecode matches
on Sourcify.
Prerequisites: Node.js 22+, npm 10+ and Foundry.
git clone https://github.com/Hijanhv/knot-hook.git
cd knot-hook
npm install
npm run check
npm test
npm run devOpen http://localhost:3000. The quote inspector reads the live federation on Unichain Sepolia;
if the public RPC is unavailable, it switches to the deterministic fixture and labels that data
as a reference calculation.
Run focused contract suites from the workspace root:
npm run test:unit --workspace @knot/contracts
npm run test:integration --workspace @knot/contracts
npm run test:security --workspace @knot/contracts
npm run test:invariant --workspace @knot/contracts
npm run test:coverageRun the full transaction lifecycle on a disposable Anvil chain:
cd packages/contracts
anvil --port 8550 --chain-id 31337 --block-time 1 --gas-limit 200000000 --silent &
knot_anvil_pid=$!
trap 'kill "$knot_anvil_pid"' EXIT
forge script script/local/LocalE2E.s.sol:LocalE2E \
--rpc-url http://127.0.0.1:8550 \
--unlocked \
--sender 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 \
--broadcast \
--slowSuccess ends with LOCAL_E2E_OK after two members deploy and seed, all four swap branches execute,
pending liquidity activates and refunds, LP shares withdraw, and every reserve/custody identity is
checked again.
forge test runs 204 passing cases across 20 suites. The KNOT source reports 99.32% line,
96.95% statement, 84.62% branch and 100% function coverage.
| Area | Evidence |
|---|---|
| Quote math | Independent integer oracle, both directions, both swap modes, rounding and boundary values |
| Hook integration | Canonical v4 PoolManager execution, settlement rollback, native ETH and custody identities |
| Federation safety | Authentication, code identity, membership churn, aggregate equality and atomic multi-member batches |
| Liquidity safety | Pending isolation, maturity boundaries, activation ratio, refund ownership and exit lock |
| Adversarial behavior | Sandwiches, backruns, slicing, cycles, donation, JIT liquidity, buddy pools and coalition influence |
| Stateful checks | Five high-depth invariant properties and 163,840 calls with zero handler reverts |
| Runtime | 13,801-byte hook, leaving 10,775 bytes below EIP-170 |
| Gas | 41,458-gas hook overhead against the matching hookless v4 fixture |
Detailed suite ownership is in the public testing guide; the bounded economic results and methodology are summarized in the results guide.
The tests deliberately preserve results that are inconvenient but important:
- a balanced same-member sandwich remains profitable at about 0.393701 token0 with KNOT, compared with 1.803279 token0 in the hookless fixture;
- a coalition-controlled member can loosen the aggregate boundary by 4,722 bps in the constructed capital-bounded scenario;
- pools outside the federation bypass the boundary;
- corrective flow can be clipped because KNOT selects quote direction, not trader intent;
- the aggregate curve is a virtual policy boundary, not a fair-price oracle or executable cross-pool route.
KNOT is therefore a deterministic reserve-aware boundary for participating pools—not universal MEV prevention.
knot-hook/
├── apps/
│ ├── web/ Next.js product and quote inspector
│ └── docs/ Mintlify documentation
├── packages/
│ └── contracts/
│ ├── src/{core,hooks,libraries}/
│ ├── script/{deploy,local,verify}/
│ ├── test/{unit,integration,security,invariant,benchmark,fixtures}/
│ └── lib/ Pinned contract dependencies
├── docs/
│ ├── protocol/ Design and mechanism notes
│ ├── assurance/ Verification guide
│ ├── research/ Economic methodology and results
│ └── submission/ Demo and form preparation
├── assets/readme/ Repository visuals
├── SECURITY.md Trust assumptions and unsupported assets
└── package.json Workspace command surface
Supported now:
- one owner-approved federation for one canonically sorted pair;
- exact-input and exact-output swaps in both directions;
- conventional ERC-20 pairs and native ETH;
- bounded membership and two-stage LP maturity;
- custom accounting against the canonical v4 PoolManager.
Explicitly unsupported:
- fee-on-transfer, rebasing or callback-bearing ERC-20s;
- permissionless federation membership;
- unregistered pools, cross-pair or cross-chain strategies;
- an external fair-price guarantee;
- coalition-proof or universal MEV protection;
- production use without an independent audit.
Read SECURITY.md before changing hook permissions, membership, reserve accounting, settlement or LP lifecycle logic.