You are building a submission for the Hook the Future hackathon (OKX/X Layer + Uniswap + Flap). Deadline: 2026-05-28 23:59 UTC — roughly 48 hours from now. Quality and a working demo matter more than feature breadth.
Project context lives at ~/brain/projects/AGENTFLOAT.md. Read it first.
The project workspace has 11 official Uniswap skills pre-installed at .agents/skills/. Use these — do not write hooks from scratch. The relevant ones for this build:
| Skill | When to use |
|---|---|
v4-hook-generator |
Generate the hook contract via OpenZeppelin MCP — use this FIRST to scaffold AgentFloatHook.sol |
v4-security-foundations |
Run IMMEDIATELY after hook generation to audit permissions, delta accounting, access control. Mandatory. |
v4-sdk-integration |
For wiring the off-chain agent to v4 |
viem-integration |
For the brain agent (agent/src/) — use this stack |
deployer |
For deploy scripts |
liquidity-planner |
For detecting out-of-range LP positions |
swap-planner |
For JIT recall logic in beforeSwap |
configurator |
For hook permission config |
The skills are universal — they work in Antigravity, Codex, Gemini CLI, Claude Code, and 8 others.
Build order with skills:
- Invoke
v4-hook-generator→ scaffoldAgentFloatHook.solwith the right permissions for our case (afterAddLiquidity + beforeSwap) - Invoke
v4-security-foundations→ audit the generated hook - Use
configurator→ set hook flags correctly - Use
deployer→ deploy script generation - Use
viem-integration→ off-chain brain agent - Use
liquidity-planner+swap-planner→ logic for range detection and recall
A Uniswap v4 hook + yield vault system on X Layer testnet where:
- Out-of-range LP USDC flows from the pool into a vault via the hook
- The vault runs multiple yield strategies in parallel — one
active(deploys real funds), othersshadow(simulated against the same conditions but with no real capital) - An off-chain brain agent scores shadow strategies on rolling windows and writes scores to a journal file
- When a shadow strategy beats the active one for N epochs, the agent calls
promote()on-chain and the shadow becomes the new active strategy - The brain keeps testing new variants in parallel
Tagline: "Yield routing that learns and ships its own upgrades."
agentfloat-hook/
├── PRD.md # this file
├── README.md # public-facing project description
├── contracts/ # Foundry project
│ ├── foundry.toml
│ ├── remappings.txt
│ ├── lib/
│ │ ├── openzeppelin-contracts/
│ │ ├── v4-core/
│ │ └── v4-periphery/
│ ├── src/
│ │ ├── AgentFloatHook.sol
│ │ ├── FloatVault.sol
│ │ ├── interfaces/
│ │ │ └── IStrategy.sol
│ │ └── strategies/
│ │ ├── IdleStrategy.sol
│ │ └── MockYieldStrategy.sol
│ ├── script/
│ │ └── Deploy.s.sol
│ └── test/
│ ├── AgentFloatHook.t.sol
│ └── FloatVault.t.sol
├── agent/ # off-chain brain agent
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
│ ├── index.ts # entry point
│ ├── watcher.ts # listens to vault events
│ ├── scorer.ts # scores shadow strategies
│ ├── promoter.ts # calls promote() when threshold met
│ └── config.ts
├── web/ # demo dashboard (single-page Next.js or even static)
│ ├── package.json
│ └── src/
│ └── app/
│ └── page.tsx # live vs shadow strategy comparison
├── docs/
│ ├── architecture.md
│ └── submission.md # final submission text + links
└── .env.example
interface IStrategy {
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external returns (uint256 actualOut);
function currentValue() external view returns (uint256);
function asset() external view returns (address);
function name() external view returns (string memory);
}Extend the existing FloatVault at ~/Documents/vibecoding/float-yield-router/contracts/contracts/FloatVault.sol (61 lines) with:
struct StrategyEntry {
address strategy; // address of IStrategy implementation
bool isActive;
bool isShadow;
uint256 totalDeposited; // tracked separately from real deposits
uint256 lastScore; // last score posted by the brain agent
uint256 lastScoreAt;
}
mapping(uint256 => StrategyEntry) public strategies;
uint256 public activeStrategyId;
uint256 public strategyCount;
address public promoter; // address authorized to call promote()
event StrategyRegistered(uint256 indexed id, address strategy, string name);
event StrategyPromoted(uint256 indexed fromId, uint256 indexed toId, uint256 atBlock);
event ScoreUpdated(uint256 indexed strategyId, uint256 score, uint256 epoch);
function registerStrategy(address strategy, bool asShadow) external onlyOwner returns (uint256 id);
function postScore(uint256 strategyId, uint256 score) external; // callable only by promoter
function promote(uint256 strategyId) external; // callable only by promoter
function park(uint256 amount) external; // unchanged, but routes to activeStrategyId
function withdraw(uint256 amount) external; // unchanged, but pulls from activeStrategyIdPromotion logic: when promote(id) is called, the vault should:
- Withdraw all funds from current active strategy
- Deposit them into the new active strategy
- Update
activeStrategyId - Emit
StrategyPromotedevent
A minimal Uniswap v4 hook:
import {BaseHook} from "v4-periphery/utils/BaseHook.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";
contract AgentFloatHook is BaseHook {
FloatVault public immutable vault;
constructor(IPoolManager _poolManager, FloatVault _vault) BaseHook(_poolManager) {
vault = _vault;
}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: false,
afterInitialize: false,
beforeAddLiquidity: false,
afterAddLiquidity: true, // detect range, route idle
beforeRemoveLiquidity: false,
afterRemoveLiquidity: true,
beforeSwap: true, // JIT recall on swap
afterSwap: false,
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: false,
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
// implement _afterAddLiquidity: if position is out of range, pull idle USDC into vault.park()
// implement _beforeSwap: if swap moves price back into LP range, call vault.withdraw() for JIT
}Note: The actual mechanics of detecting out-of-range LP and routing USDC are non-trivial in v4 due to flash accounting. For the MVP, stub the logic with a clear comment if full implementation isn't feasible in 48h. The judges should see the architecture clearly even if the exact accounting is simplified.
IdleStrategy— just holds USDC,currentValue()returns raw balance. Baseline.MockYieldStrategy— accrues simulated yield at N bps per block (useblock.numbersince last update). For the demo, this represents what a real yield protocol would do.
Both implement IStrategy.
A Node/TypeScript long-running script. Stack: viem for RPC, dotenv for config, plain Node for file writes.
Entry point. Starts the watcher loop.
- Connects to X Layer testnet RPC
- Listens to
FloatVaultevents:Parked,Withdrawn,ScoreUpdated - On each event, fetches current state and triggers scoring
For each registered strategy (active + shadow):
- Compute
score = currentValue / totalDeposited(simple yield ratio) - Optional: penalize for high recall latency (track via block delta on
Withdrawn) - Append a line to
~/brain/raw/agentfloat-strategy-scores.md:[2026-05-27 14:32:01] epoch=42 strategy=1 (MockYield) score=10042 active=true [2026-05-27 14:32:01] epoch=42 strategy=2 (IdleStrategy) score=10000 active=false - Call
postScore(strategyId, score)on the vault for each strategy
- Tracks rolling N-epoch performance per strategy
- If a shadow strategy beats the active strategy by
MIN_DELTA_BPSforMIN_EPOCHSconsecutive epochs, callsvault.promote(shadowId) - Logs the promotion to
~/brain/journal/YYYY-MM-DD.md
export const CONFIG = {
rpcUrl: process.env.X_LAYER_RPC_URL!,
vaultAddress: process.env.VAULT_ADDRESS! as `0x${string}`,
hookAddress: process.env.HOOK_ADDRESS! as `0x${string}`,
promoterPrivateKey: process.env.PROMOTER_PRIVATE_KEY!,
pollIntervalMs: 5000,
minEpochsToPromote: 5,
minDeltaBps: 10,
};A single Next.js page (App Router) showing:
- Vault total deposits
- Current active strategy: name, current value, total deposited
- Shadow strategies: name, simulated current value, score delta vs active, "would be promoted in X more epochs" counter
- Recent events feed (Parked, Withdrawn, ScoreUpdated, StrategyPromoted) — last 20
- Reads directly from X Layer testnet via viem; no backend needed
Style: clean, editorial, near-cream background (#F4F0E8), JetBrains Mono for numbers, no animations. Lean on the same aesthetic as the Claude portfolio at ~/Documents/vibecoding/portfolio-redesign/claude/index.html.
Foundry script that:
- Deploys
FloatVaultwith placeholder USDC address (or a mock USDC if X Layer doesn't have native USDC) - Deploys
IdleStrategy+MockYieldStrategy - Registers both with the vault (Idle as active, MockYield as shadow)
- Deploys
AgentFloatHookwith the correct hook permission flags encoded in the address (useHookMinerfrom v4-periphery) - Outputs all addresses to
deployments/x-layer-testnet.json
X_LAYER_RPC_URL=
X_LAYER_CHAIN_ID=
DEPLOYER_PRIVATE_KEY=
PROMOTER_PRIVATE_KEY=
USDC_ADDRESS=
POOL_MANAGER_ADDRESS=
Check whether X Layer has Uniswap v4 PoolManager deployed. If not, deploy a fresh PoolManager from v4-core as part of the deploy script. Document the address.
In docs/submission.md:
- Contract addresses table
- X Layer explorer links for each
- Demo video link (90 sec, YouTube unlisted is fine)
- Landing page URL (Vercel deploy)
- Twitter thread URL (tagging
@XLayerOfficial,@Uniswap,@flapdotsh) - GitHub repo (this directory pushed to github.com/ronkenx9/agentfloat-hook)
- One-line description for the Google Form
- Hour 0–4 — Foundry init, install v4-core + v4-periphery as submodules, write
IStrategy+IdleStrategy+MockYieldStrategy, basic Forge tests - Hour 4–10 —
FloatVaultv2 with strategy registry, promotion logic, full Forge test coverage - Hour 10–18 —
AgentFloatHookskeleton, hook permissions, deploy script with HookMiner, deploy to X Layer testnet - Hour 18–26 — Off-chain brain agent (
agent/), watcher + scorer + promoter loops, test against live testnet contracts - Hour 26–34 — Demo dashboard (
web/), Vercel deploy - Hour 34–42 — Demo video, tweet thread, landing copy, submission form
- Hour 42–48 — Buffer for fixes, polish, deployment retries
- No new untested ideas during build. If something in the architecture turns out to be infeasible, stub it with a clear comment and document the limitation — don't pivot mid-build.
- Demo must work end-to-end. A non-running demo loses to a simpler running demo.
- Test the hook deployment early. Hook address mining can take time; do it in hour 4, not hour 40.
- Keep the strategy interface narrow. Two strategies (Idle + MockYield) is enough to tell the story. Don't add a third.
- No Mantle, no Pharos, no Swarms. This is X Layer only. Ship one thing well.
The submission is a winner if:
- The demo video clearly shows: LP capital → vault → live strategy → shadow strategy scoring → automatic promotion
- A judge can read the README and understand the system in 60 seconds
- The contracts compile, deploy, and the
promote()flow has been executed at least once on testnet - The Twitter thread frames it as "yield routing that learns" — not just another yield hook
That's the bar. Anything beyond that is bonus.