# Strategy Token Hook for Hookr

Permissionless strategy tokens that hold a Uniswap v4 pool of a base asset and pay redeemable claim tokens. Integration guidelines for the Hookr team.

Status: LIVE on Robinhood Chain 4663 since 2026-10-13 (redeployed on the HK-22 partial fill fix bytecode, partner leg kept). StrategyLauncher `0x9f6821af226D85234DED5F0f024A8A2B4Fa6d023`, strategy StonkFeeHook `0x242FA7F53383a0e77Dd2A7778dC9Da794f042AcC`. Unit suite 25/25 green, whole `hooks/` suite 142/142 on the deployed bytecode. Audit findings HK-10 to HK-18 were closed before the deploy (section 8). Sourcify exact match on both contracts. The Hookr fee leg (section 3.1) is live with the recipient left at zero until Hookr supplies its contract address.

Source: `hooks/` in the StonkBrokers repository. Contracts `StrategyLauncher.sol`, `StrategyToken.sol`, shared `StonkFeeHook.sol`. ABIs: `StrategyLauncher.abi.json`, `StrategyToken.abi.json`, `StonkFeeHook.abi.json`.

Contact: simple@clutch.market, Telegram simplefarmer69.

## 1. What the hook does

A creator launches a strategy token against a listed base (WETH or an allowlisted ERC-20). Buyers mint strategy shares by depositing quote. The launcher opens a full range v4 pool behind `StonkFeeHook` and locks liquidity. Trade fees and a creator cut accrue. Holders can redeem strategy shares for underlying, or claim streamed fees via a claim token.

Design goals:

- Locked LP for the life of the strategy (outside add/remove refused).
- Redeemable shares (no permanent trap of depositor capital at the share layer).
- Claim token for fee stream without forcing full exit.
- Owner listed bases only (no arbitrary hostile ERC-20 as base without an allowlist step).

Threat model: Robinhood Chain has no public mempool. Frame mitigations as thin pool / stale mark guards and launch economics, not sandwich protection.

## 2. Why this is not an owned root on Hookr

Same as the Battle Token Hook: custom controller + `StonkFeeHook` is a third party hook. Analyze / Publish / Partner Review path. Swaps use Universal Router with empty hookData. Hookr SDK root only paths do not apply.

## 3. Addresses

| Item | Chain | Address |
| --- | --- | --- |
| StrategyLauncher | Robinhood Chain 4663 | 0x9f6821af226D85234DED5F0f024A8A2B4Fa6d023 |
| StonkFeeHook (strategy instance) | Robinhood Chain 4663 | 0x242FA7F53383a0e77Dd2A7778dC9Da794f042AcC |
| WETH (first listed base) | Robinhood Chain 4663 | 0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73 |
| Uniswap v4 PoolManager | Robinhood Chain 4663 | 0x8366a39CC670B4001A1121B8F6A443A643e40951 |
| Uniswap V4 Quoter | Robinhood Chain 4663 | 0x8dc178efb8111bb0973dd9d722ebeff267c98f94 |
| Universal Router 2.1.1 | Robinhood Chain 4663 | 0x8876789976dEcBfCbBbe364623C63652db8C0904 |

Deploy parameters (state file `hooks/deployments/strategy-hook.4663.json`): owner `0xD4aF10Bc56b08f8fc2990a3ADeeD4b757ae6A26C`, protocolTo = treasury `0xb668382cF44038a3E8140E789060F6A809787CDa`, protocolBps 1000, partnerTo `0x0` (pending the Hookr CA), partnerBps 5000, minSeedQuote 0.05 WETH, minExecuteQuote 0.001 WETH (1e15, owner tunable), exec guards maxExecSlipBps 300 / maxExecImpactBps 100 (defaults), hook flags 10956. Deploy txs: launcher `0x1f5911c0b9d3be8e67127ad9b3b307b33195aab4beb76e5099a1c00b614558ff`, hook `0x87b33b024878a7c713f85b0f0c8b0ff141f6ea2a70a2f4b48c8ba905bc3eef4d`, setHook `0xf7970a1e1d6a5b3c698ffd81cfcf1734c73c3889b5f9a7742c42c7f2361ef631`, setPartner `0x644aa624558ffb865b5a44f79ab53899ec2b83715d17325fecc390e022bc4cdd`, setMinExecuteQuote `0x50c96fd7ca23177800b44623b2c5d10ee863df3d6b096f609052939cd42faff9`. Verified: Sourcify exact match (https://sourcify.dev/server/v2/contract/4663/0x9f6821af226D85234DED5F0f024A8A2B4Fa6d023).

Deployed size: StrategyLauncher 22,971 B (93.5% of EIP-170), StonkFeeHook 5,596 B.

### 3.1 Hookr fee leg

Same split as the Battle Token Hook: `protocolCut = fee x protocolBps / 10000`, then `partnerCut = protocolCut x partnerBps / 10000` is carved out of the protocol cut for Hookr (5000 bps = half of the protocol cut). The StonkBrokers treasury keeps the other half, and the remainder of the fee accrues as `pending` on the launcher for `execute`. While `partnerTo` is zero the hook books the carve under `partnerAccrued[quote]`; once the owner runs `StrategyLauncher.setPartner(<hookrCA>, 5000)` anyone releases it with `StonkFeeHook.flushPartner(<quote>)` and later swaps pay Hookr directly inside the swap. Events: `PartnerFeeTaken`, `PartnerFlushed`, `PartnerSet`.

## 4. Hook identity and hooklist entry

Flags match `StonkFeeHook` (beforeInitialize, beforeAdd/RemoveLiquidity, before/afterSwap, before/afterSwapReturnsDelta).

```json
{
  "name": "Strategy Token Hook",
  "author": "StonkBrokers",
  "address": "0x242FA7F53383a0e77Dd2A7778dC9Da794f042AcC",
  "chainId": 4663,
  "dynamicFee": false,
  "upgradeable": false,
  "requiresCustomSwapData": false,
  "vanillaSwap": true,
  "beforeSwapReturnsDelta": true,
  "afterSwapReturnsDelta": true,
  "source": "https://github.com/Clutch-L4bs/stonkbrokers/tree/main/hooks",
  "docs": "StonkBrokers-Strategy-Token-Hook-Hookr-Integration.pdf",
  "tags": ["strategy", "redeemable", "claim", "robinhood-chain"]
}
```

## 5. Pool keys

| Field | Value |
| --- | --- |
| currencies | strategy token and base, sorted |
| fee | 10000 (1%) typical |
| tickSpacing | 200 |
| hooks | StonkFeeHook (strategy instance) |

## 6. Swapping and redeem

Quote with V4 Quoter, empty hookData. Redeem and claim paths are launcher / token methods, not pool swaps. Surface them as product actions beside the trade desk.

## 7. Contract functions

What each public surface does. Arguments match the shipped ABIs (`StrategyLauncher.abi.json`, `StrategyToken.abi.json`, `StonkFeeHook.abi.json`).

### 7.1 StrategyLauncher

Launch and fee buyback:

| Function | Who | What it does |
| --- | --- | --- |
| `createStrategy(params)` | creator | Pulls `seedQuote`, deploys a new `StrategyToken` (fixed supply into the pool), opens a locked full range v4 pool behind the strategy `StonkFeeHook`, registers the optional buyback `route` PoolKey (must already exist when target != quote). Returns `(strategyId, token)`. Reverts `HookMismatch`, `NativeNotSupported`, `SeedTooSmall`, `BadParams`, `BadRoute`. Emits `StrategyCreated`. |
| `execute(strategyId, minOut)` | anyone | Permissionless. Swaps accumulated `pending` quote for the strategy `target` through the stored route (or identity when target == quote), transfers target into the StrategyToken and `notify`s holders. Reverts `NothingPending` while `pending < minExecuteQuote`. The swap runs in TRANCHES: each tranche is sized so its price impact on the route pool stays under `maxExecImpactBps`, and every tranche carries a floor derived from the pool price minus `maxExecSlipBps`; the caller `minOut` is checked against the total. A quoted `minOut` that no longer clears reverts `SlippageExceeded(got, minOut)` and leaves `pending` untouched, so a failed execute never loses value. Reverts `UnknownStrategy`. Emits `Executed(strategyId, quoteIn, targetOut)`. |
| `withdrawLiquidity(strategyId)` | creator | After `lpUnlockAt` (must be set and reached), pulls the whole locked position once. Reverts `NotCreator`, `LiquidityLocked`. Emits `LiquidityWithdrawn`. |

Read surface:

| Function | Who | What it does |
| --- | --- | --- |
| `strategy(id)` | anyone | Full `Strategy` struct: creator, token, quote, target, pool, route, fees, snipe schedule, pending / totalFees / totalBought, unlock clocks, liquidityPulled. |
| `currentSnipeBps(id)` | anyone | Live decaying snipe tax from launch time. |
| `strategyOf(poolId)` | anyone | Reverse lookup pool to strategy id. |
| `strategyCount` / `minSeedQuote` / `minExecuteQuote` / `protocolTo` / `protocolBps` / `partnerTo` / `partnerBps` | anyone | Globals, including both fee destinations (StonkBrokers protocol leg, Hookr partner leg). |
| `feeTerms(poolId)` / `onSwap(...)` | StonkFeeHook | Controller callbacks. Outside LP locked. Fee remainder after creator cut accrues as `pending` for `execute`. Emits `FeeAccrued`. |

Owner (no path to locked LP principal or pending pots):

| Function | Who | What it does |
| --- | --- | --- |
| `setProtocol(to, bps)` | owner | Protocol cut of the controller fee. Emits `ProtocolSet`. |
| `setPartner(to, bps)` | owner | Hookr partner leg: `bps` of the PROTOCOL cut for `to` (default `0x0` / 5000 at deploy). Reverts `PartnerBpsTooHigh` above 10000. Emits `PartnerSet`. |
| `setMinSeedQuote(v)` / `setMinExecuteQuote(v)` | owner | Launch and execute floors. Emits `MinExecuteSet`. |
| `setExecGuards(slipBps, impactBps)` | owner | Execute guard rails: per tranche slippage floor (max `MAX_EXEC_SLIP_BPS` 2000) and per tranche price impact cap (max `MAX_EXEC_IMPACT_BPS` 1000). Defaults 300 / 100. Reverts `BadParams` above the caps. Emits `ExecGuardsSet`. |
| `setHook(hook)` | owner | Wire the strategy `StonkFeeHook`. |
| `transferOwnership` / `acceptOwnership` | owner / pending | Two step Ownable. |

### 7.2 StrategyToken

Share ERC-20 that streams the buyback target to holders. Launcher-gated mint/exclude; holders claim.

| Function | Who | What it does |
| --- | --- | --- |
| ERC-20 surface | anyone | `name` / `symbol` / `decimals` / `totalSupply` / `balanceOf` / `transfer` / `approve` / `transferFrom`. |
| `claimable(holder)` | anyone | Unclaimed target token amount for `holder` (rewards-per-share accounting). |
| `claim()` / `claimTo(to)` | holder | Pays claimable target to the caller or `to`. |
| `burn(amount)` | holder | Burns strategy shares from the caller. |
| `notify(amount)` | launcher only | Credits a new drip after `execute`. |
| `setExcluded(account, value)` | launcher only | Excludes an address from the drip (PoolManager is excluded at launch). |

### 7.3 StonkFeeHook (strategy instance)

Same shared hook as Battle (section shape in BattleTokenHook-Hookr-Integration.md section 7.3, including `flushPartner(quote)` and `partnerAccrued(quote)`). Controller is the StrategyLauncher. Outside LP blocked while the strategy is live. Exact output reverts while a snipe tax is live.

## 8. Proofs

Unit suite `hooks/test/Strategy.t.sol` 25/25 on the deployed bytecode: launch, mint/redeem, fee accrual, strategy token, outside LP blocked, allowlist, owner bounds, multi strategy isolation, plus the audit regressions `test_execute_parksPendingWhenNoEarners`, `test_buy_creatorCutFoldsIntoPendingWhenBlocked`, `test_exactOutputRefused_zeroFeeStrategy`, `test_create_rejectsBadParams`, `test_execute_tranchesWhenPendingExceedsImpactCap` and `test_setExecGuards_boundsAndOwner`. Partner leg pinned by `hooks/test/Partner.t.sol` (9 tests on the shared hook + controller path). Whole `hooks/` suite 142/142 including the mainnet fork tests.

Audit closure (2026-10-10, fixed before deploy, report `docs/Hashlock-Certified-Audit-StonkBrokers-Hooks.pdf`):

- HK-10: the `ExactOutputWhileTaxed` guard in `StonkFeeHook.beforeSwap` sat below the zero fee early return, so an exact output swap on a zero fee pool slipped past it. The check now runs first.
- HK-11: `execute` was one market order with no floor, so a large `pending` balance could be sold into a thin route at any price (the same shape as the 2026-08 CLOCKIN bond leak). It now tranches by `maxExecImpactBps`, floors every tranche by `maxExecSlipBps`, respects `minExecuteQuote` and the caller `minOut`, and reverts with `pending` intact when a fill is too thin.
- HK-13: `snipeStartBps + max(buyFeeBps, sellFeeBps) >= 10000` is rejected at `createStrategy` (`BadParams`), a 100% tax can no longer be configured.
- HK-17 (was H-1 / H-2): a fee push while `totalEarningSupply == 0` (no holder outside the excluded set yet) used to strand the quote. `StrategyToken` now parks it in `pendingReward` and `_materializePending` folds it into the reward index on the first earning balance, so nothing is lost and nothing is double counted.
- HK-18 (was H-3): the creator cut was a hard `_safeTransfer`, so a reverting or blacklisted creator wallet could brick every taxed swap. `onSwap` now uses the tolerant `_tryTransfer` and folds a failed creator leg into the buyback pending balance (`FeeAccrued(strategyId, kept, paidCreator)` reports which path ran).

## 9. Go live status

1. StrategyLauncher + StonkFeeHook deployed 2026-10-10 via `hooks/script/StrategyHookDeploy.s.sol` on the audit fixed, partner leg bytecode (PHASE=deploy, then PHASE=verify readbacks). Two earlier same day generations with zero launches are superseded and archived: `0x936d...3217` / `0xA1EB...Aacc` (`strategy-hook.4663.superseded-2026-10-10.json`) and `0x8B7d...4a13` / `0x3369...2ACC` (`strategy-hook.4663.superseded-2026-10-10b.json`).
5. Hookr CA: owner `setPartner(<hookrCA>, 5000)` on the launcher, then anyone `flushPartner(<quote>)` on the hook. Pending on Hookr.
2. WETH is the first listed base; further bases are owner `setBase` calls.
3. Addresses above are final; the hooklist entry is live. Pool keys come from `StrategyCreated` events.
4. Launching and trading strategies is permissionless; the owner only lists bases and tunes bounded fee knobs.
