Build a hook
Write a custom hook for hookit coins with the AI coding agent you already use (Claude Code, Cursor…): hand it a brief, it writes the plugin on the SDK's starter and runs the conformance harness hookit's hook is held to. You deploy it on Ink with your own wallet and submit it; once hookit checks it and registers it, every launch that picks it pays you 20% of what it spends.
Three kinds of hook
Swap rules and fees
Inside every swap: refuse a buy, guard sells from MEV bots, cap a trade, or add a fee that lands in the coin's hook pot.
Spending the hook pot
Spends the share of the pot its creator gave it: burns, LP, the floor, holders, the creator, and 20% of it to you.
Both, in one plugin
A rule or a fee on the trades and what its share of the pot pays for: one contract, one review, one registration.
A plugin is a small contract on its own. hookit's hook calls it on the coins whose creators picked it, with exactly the gas it declared, and applies its answer within the caps registered on chain: a fee never above what it declared or the coin's 10% cap, a spend never above its share. It never holds the coin's money, and nothing in hookit is redeployed for a new plugin.
Install the SDK
A Foundry project with the plugin interface, two examples, a starter with TODOs, a conformance harness and a live Ink fork test. You need Foundry and git.
git clone https://github.com/SDKHookit/SDKHookit hookit-plugin-sdk && cd hookit-plugin-sdk
./setup.sh # v4-core and forge-std into ./lib
forge build && forge test # the examples, the starter and the harness, green1Give your agent the brief
Open the SDK folder in your coding agent (Claude Code, Cursor…) and paste the brief for what you are building. It holds every rule hookit's review checks, how to test it, and tells your agent never to touch a key or broadcast anything itself: you deploy, or hookit-mcp does from its own wallet when you ask.
You are helping me write a plugin for hookit that runs on a coin's trades (a rule or an extra fee on its swaps), and submit it to the hookit
Workshop. I write, test and deploy it with my own wallet on Ink; hookit reviews it and registers it.
You never broadcast a transaction and never ask for, print or store a private key or seed phrase.
The one exception is hookit-mcp, if I connected it to you: its own wallet signs, you never see its
key, and you use its signing tools only when I ask, after their dry run (see DEPLOY).
Read every rule below before writing code.
HOOKIT: a Uniswap v4 launchpad on Ink (chain 57073). Every coin trades in a v4 pool whose hook is
hookit's Master hook: hook D for every launch since 2026-10-16, hook C for earlier coins. Both run
the same MasterLaunchHookC code, so "hook C" below means either. A coin's creator picks plugins at
launch; hook C calls each one at the
points it subscribes to, with exactly the gas it declared, and applies its answer within hard
bounds. A plugin never holds the coin's money and never calls the pool. Once hookit registers it
in the ModuleRegistry, it shows on the Workshop board and in the launch form; nothing is redeployed.
LIVE ON INK (chain 57073, RPC https://rpc-gel.inkonchain.com, explorer https://explorer.inkonchain.com):
ModuleRegistry 0x91f1622D49a09Ce2904E8f98650393eB3dAA2470 every plugin's constructor argument
Master hook D 0x8a29B2c508007707bbFF1814386AA968bbeCbFff new launches (HookRegistry default)
Master hook C 0x4b533C432516FC1Be4b0bAEFe6BFF0942AD87FfF coins launched before 2026-10-16
Factory C 0x145c3F3560c23fDA93eA79f2EfeB7f33D8A9Ff3e
PoolManager 0x360E68faCcca8cA495c1B759Fd9EEe466db9FB32
THE SDK (Foundry; solc 0.8.26, EVM cancun, via-IR):
git clone https://github.com/SDKHookit/SDKHookit hookit-plugin-sdk && cd hookit-plugin-sdk && ./setup.sh
forge build && forge test
Read, in order: AGENTS.md (its rules are hookit's review checklist), src/hookit/IHookPlugin.sol
(the exact interface, plugin API v3), src/examples/SwapCounterPlugin.sol (moves no money),
src/examples/BuyBurnPlugin.sol (spends safely), src/MyPlugin.sol (the starter), MANIFEST.md (what
I submit). Never edit src/hookit/* or test/harness/*. Start from src/MyPlugin.sol: rename the
contract and its Declaration library, rename its test, and add it to script/Manifest.s.sol.
HOOK POINTS: 0 onLaunch (once per pool; reads the creator's initData) · 1-2 initialize · 3-4 add
liquidity · 5-6 remove liquidity · 7-8 swap (before / after) · 9-10 donate. A plugin subscribes to
a point with that point's bit in caps and by overriding its callback; hook C never calls a point it
did not subscribe to. A callback that reverts, runs out of gas or answers in a wrong shape reverts
the user's swap (fail-closed): every trade of every coin using it stops until hookit halts it.
Points 5-6 are the exception: a faulty remove callback is skipped.
LAUNCH SETTINGS: onLaunch reads the creator's initData. hookit.fun's launch form sends it empty, so
give every setting a default when initData.length == 0; an agent launching through hookit-mcp or
the agent API can pass up to 1,024 bytes. Decode it once in onLaunch and keep it by pool.
ON THE TRADES (points 7-8: CAP_BEFORE_SWAP bit 15, CAP_AFTER_SWAP bit 16).
beforeSwapPlugin(SwapCtx) returns a BeforeSwapResult of exactly 5 fields:
guard 0 allow, 1 block. Blocking a buy needs CAP_GUARD (bit 17). Blocking or capping
a sell needs CAP_SELL_GUARD_ANTIMEV (bit 18) and is only accepted as a
time-bounded or size-only anti-MEV rule.
reason your code, surfaced in the PluginBlocked event.
surchargeBps an extra fee on this swap, into the coin's hook pot: CAP_FEE_SURCHARGE (bit 19),
at most maxSurchargeBps (900 at most) and never above ctx.feeHeadroomBps (what is
left of the coin's 10% fee cap).
maxQuoteNotional 0 = no cap, else the largest trade it lets through, in quote.
rebate quote handed back to the swapper: CAP_BEFORE_SWAP_DELTA (bit 28), at most
ctx.rebateHeadroom and ctx.available.
SwapCtx gives isBuy, exactInput, quoteNotional (the trade's pre-fee size in quote), the price and
tick before the swap, hookTaxBps, sender, origin and hint. The trader is ctx.hint when it is not
zero, else ctx.origin; both are untrusted, so never let a sell change anyone's state (a seller can
name anyone in hookData).
Write every rate, threshold and window as a named constant, one per tier, compared in one place,
and make each branch say its side (a buy, a sell): the review and the board read them as exact rows
("Buys over 1 ETH: +2% into the pot").
A surcharge is reserved in the 10% fee cap of every coin that uses the plugin and lowers its
creator's maximum hook tax: declare the smallest maxSurchargeBps the mechanic needs, 0 if none.
THE RULES (hookit's review; the harness checks the ones marked H):
- Never revert on valid input (H): clamp, saturate or return early. FullMath.mulDiv for products;
test 1e27-wei supplies and 6, 8 and 18-decimal quotes. Reverting in onLaunch on bad creator
parameters is fine: it fails the launch, not trading.
- No call to the PoolManager or hook C, and no external call at all in the swap callbacks.
- Bounded gas (H): no loop over users, rounds or positions; no gasleft() logic.
- Declared gas at least 2x the measured worst case at every point, cold storage included (H);
maxIntents is the most the code can return at that point.
- Exact return shapes (H), and guards, surcharges and rebates only with their capabilities (H).
- Inherit HookPluginBase and call _auth(ctx.poolId) first in every callback; key all state by
(msg.sender, ctx.poolId). msg.sender is hook C, never a user.
- No owner, admin, setter, kill switch, pause that reverts, upgrade, proxy, DELEGATECALL,
SELFDESTRUCT, approvals or randomness: immutable code with owner-controlled state is refused.
- No guard that traps holders: refuse a sell only as a time-bounded or size-only anti-MEV rule
(CAP_SELL_GUARD_ANTIMEV), never by allowlist, blocklist, origin or sender.
- The verified source must compile to the deployed codehash, at the commit you link.
- Halt-safe: hookit can halt a plugin; anything users are owed stays claimable without a callback.
- Compatibility declared honestly: family groups, forbidden built-ins, incompatible modules
(AGENTS.md, rule 13).
TEST before submitting, on my machine:
forge test the harness, your unit and fuzz tests
forge test --match-test test_conformance_gasReport -vv measured gas: declare >= 2x, rerun
INK_RPC_URL=https://rpc-gel.inkonchain.com forge test --match-path 'test/fork/*' -vv
the real hook C, on an Ink fork
forge fmt
One unit test per rule of the mechanic and fuzz tests for every money path (paid <= funded). Never
weaken the harness to get green: a harness failure is a real hook C failure.
DEPLOY: I run these, with my own wallet. You prepare them and never run one that broadcasts.
cast wallet import plugin-dev --interactive # once; I type the key, stored encrypted
forge create src/MyPlugin.sol:MyPlugin --constructor-args 0x91f1622D49a09Ce2904E8f98650393eB3dAA2470 \
--rpc-url https://rpc-gel.inkonchain.com --account plugin-dev --broadcast
forge verify-contract <address> src/MyPlugin.sol:MyPlugin --chain 57073 \
--constructor-args $(cast abi-encode "f(address)" 0x91f1622D49a09Ce2904E8f98650393eB3dAA2470) \
--verifier blockscout --verifier-url https://explorer.inkonchain.com/api/
forge script script/Manifest.s.sol --sig "run(string,address)" my-plugin <address> \
--rpc-url https://rpc-gel.inkonchain.com
The last one reads the deployed code, re-runs every registry check and writes
manifests/my-plugin.json; it never broadcasts. Push the code to a public repository at the exact
commit I deployed.
OR THROUGH HOOKIT-MCP (0.7.0 or later, with its own wallet on Ink), when I ask you to deploy:
deploy_hook { "artifact": "<SDK folder>/out/MyPlugin.sol/MyPlugin.json" } dry run: gas, address
deploy_hook { "artifact": "<SDK folder>/out/MyPlugin.sol/MyPlugin.json", "dryRun": false } deploys it
(absolute paths: the server runs in its own folder). It passes the ModuleRegistry as the constructor
argument itself. Then run verify-contract and the Manifest script above with the address it
returns (neither broadcasts).
SUBMIT: https://hookit.fun/workshop/submit, four steps. I say where it acts (on swaps), drop
manifests/my-plugin.json and give the repository link, then a name, a one-line tagline, a
description, a logo and its extra listings (AI agents, Special), and sign one message with the
wallet that deployed it (no transaction, no fee). hookit refuses it at once unless it is deployed
on Ink, speaks plugin API v3 and is immutable, and shows on its card whether it matches its
codehash, has verified source and passes the registry's own checks; then it reads the code and
the tests against AGENTS.md, may ask for changes (a change is a new deployment and a new
manifest), and registers it. Its card on the Workshop board then shows what
it takes, where its money goes and every check.
Through hookit-mcp, submit_hook { "manifest": "<SDK folder>/manifests/my-plugin.json", "repo", "name",
"tagline", "description", "tags", "logo" } is the same submission, signed by the wallet that
deployed it: dry run first, then "dryRun": false.
FOLLOW AND LAUNCH (hookit-mcp): hook_status { "plugin": "<address>" } shows where the review
stands, hookit's note and, once live, the module id. Then launch_token with "hookTaxPct": 3 and
"builderHooks": [{ "hook": <module id>, "initData": "0x..." }] launches a coin that runs it, dry
run first. Without "pct" a hook that spends the pot gets all of it; with one, the shares must total
100%: add a built-in destination for the rest ("modules": { "autoBurn": true }).
KNOW: the checks are hookit's review: never call the plugin safe or
guaranteed. hookit can halt a plugin at any time; the coin's other hooks then get its share. It is
live only once ModuleRegistry.moduleEntry(id) on Ink says so.
READ: https://hookit.fun/workshop/build (this walkthrough), AGENTS.md and MANIFEST.md in the SDK.
2Test it on your machine
The harness runs your plugin against a mock of hookit's hook with the exact gas you declared, fuzzed on three pools; the fork test registers it on a copy of Ink and trades through the real one.
forge test # the conformance harness on your plugin
forge test --match-test test_conformance_gasReport -vv # measured gas: declare at least 2x
INK_RPC_URL=https://rpc-gel.inkonchain.com forge test --match-path 'test/fork/*' -vv
# the real hook C, on an Ink fork
forge fmt3Deploy it on Ink, with your wallet
Your key stays in an encrypted keystore: never on a command line, never with your agent. The constructor argument is the live ModuleRegistry, baked into the code. Then verify the source on the Ink explorer and print the manifest you submit.
cast wallet import plugin-dev --interactive # once: your key, stored encrypted
forge create src/MyPlugin.sol:MyPlugin --constructor-args 0x91f1622D49a09Ce2904E8f98650393eB3dAA2470 \
--rpc-url https://rpc-gel.inkonchain.com --account plugin-dev --broadcast
forge verify-contract <address> src/MyPlugin.sol:MyPlugin --chain 57073 \
--constructor-args $(cast abi-encode "f(address)" 0x91f1622D49a09Ce2904E8f98650393eB3dAA2470) \
--verifier blockscout --verifier-url https://explorer.inkonchain.com/api/
forge script script/Manifest.s.sol --sig "run(string,address)" my-plugin <address> \
--rpc-url https://rpc-gel.inkonchain.com # writes manifests/my-plugin.jsonOr let your agent do it with hookit-mcp 0.7.0 or later: deploy_hook deploys the plugin from the MCP's own wallet (a dry run first), and you then verify it and print its manifest the same way. The key stays in the MCP's config; your agent never sees it.
A plugin is immutable: no owner, no proxy, no upgrade. A new version is a new deployment and a new submission.
4Submit it
Four steps on the Workshop: where it acts, its manifest and public source, its card (name, tagline, logo), then one signature from the wallet that deployed it. No transaction and no fee. Then:
- On chain, at onceDeployed on Ink, speaks plugin API v3, immutable (no proxy, no delegated wallet), its runtime codehash, verified source on the Ink explorer, a DELEGATECALL scan, and the registry's own checks on its manifest, simulated.
- The reviewhookit reads the code and the tests against the SDK's rules, and the economics: who pays, who earns, whether it can be farmed. It may ask for changes; a change is a new deployment and a new manifest.
- Registrationhookit registers it in the ModuleRegistry under the hash of its review record. From that block it is on the Workshop board, with every check above shown publicly, and in the launch form.
Through hookit-mcp, submit_hook sends the same submission signed by the wallet that deployed it,hook_status follows the review, and once it is live launch_token launches a coin that runs it, with its launch settings.
5Earn on every launch
Creators pick it in the launch form and route a share of their coin's hook pot to it. Your plugin pays you up to 20% of each spend, in the coin's quote, to the wallet set in its code at deploy. Its card shows its launches and what it has spent. hookit can halt a faulty plugin at any time; each coin's other hooks then get its share.
The checks are hookit's review. Rules in full: AGENTS.md and MANIFEST.md in the SDK, and the Workshop in the docs.


