hookr-sdk 0.2.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATING.md +89 -0
  4. package/README.md +308 -105
  5. package/dist/core/abi.d.ts +10930 -0
  6. package/dist/core/abi.js +455 -0
  7. package/dist/core/advisories.d.ts +184 -0
  8. package/dist/core/advisories.js +291 -0
  9. package/dist/core/client.d.ts +55 -0
  10. package/dist/core/client.js +107 -0
  11. package/dist/core/generated.d.ts +14295 -0
  12. package/dist/core/generated.js +1058 -0
  13. package/dist/core/index.d.ts +17 -0
  14. package/dist/core/index.js +17 -0
  15. package/dist/core/launch.d.ts +200 -0
  16. package/dist/core/launch.js +427 -0
  17. package/dist/core/pair.d.ts +64 -0
  18. package/dist/core/pair.js +136 -0
  19. package/dist/core/params.d.ts +415 -0
  20. package/dist/core/params.js +1235 -0
  21. package/dist/core/policies.d.ts +136 -0
  22. package/dist/core/policies.js +125 -0
  23. package/dist/core/proofs.d.ts +25 -0
  24. package/dist/core/proofs.js +118 -0
  25. package/dist/core/quotes.d.ts +185 -0
  26. package/dist/core/quotes.js +550 -0
  27. package/dist/core/referrals.d.ts +32 -0
  28. package/dist/core/referrals.js +36 -0
  29. package/dist/core/registry.d.ts +357 -0
  30. package/dist/core/registry.js +378 -0
  31. package/dist/core/rules.d.ts +675 -0
  32. package/dist/core/rules.js +1049 -0
  33. package/dist/core/schema.d.ts +358 -0
  34. package/dist/core/schema.js +105 -0
  35. package/dist/core/stocks.generated.d.ts +11 -0
  36. package/dist/core/stocks.generated.js +1228 -0
  37. package/dist/core/vault.d.ts +59 -0
  38. package/dist/core/vault.js +112 -0
  39. package/dist/core/wallet.d.ts +142 -0
  40. package/dist/core/wallet.js +304 -0
  41. package/dist/deployments.d.ts +8 -0
  42. package/dist/deployments.js +26 -0
  43. package/dist/index.d.ts +42 -15
  44. package/dist/index.js +40 -15
  45. package/dist/releases.d.ts +219 -0
  46. package/dist/releases.js +365 -0
  47. package/dist/wallet-cap.d.ts +159 -0
  48. package/dist/wallet-cap.js +234 -0
  49. package/package.json +46 -10
  50. package/provenance.json +31 -0
  51. package/dist/abi.d.ts +0 -6404
  52. package/dist/abi.js +0 -8300
  53. package/dist/chain.d.ts +0 -87
  54. package/dist/chain.js +0 -86
  55. package/dist/claims.d.ts +0 -32
  56. package/dist/claims.js +0 -31
  57. package/dist/fees.d.ts +0 -59
  58. package/dist/fees.js +0 -142
  59. package/dist/links.d.ts +0 -48
  60. package/dist/links.js +0 -93
  61. package/dist/market.d.ts +0 -264
  62. package/dist/market.js +0 -310
  63. package/dist/reads.d.ts +0 -136
  64. package/dist/reads.js +0 -219
  65. package/dist/rules.d.ts +0 -84
  66. package/dist/rules.js +0 -296
  67. package/dist/swap.d.ts +0 -108
  68. package/dist/swap.js +0 -144
package/README.md CHANGED
@@ -1,17 +1,17 @@
1
1
  # hookr-sdk
2
2
 
3
- Build a market on Hookr's live root hooks with a custom rule configuration, quote and swap
4
- through Hookr's router, read markets, and claim payouts, on Robinhood Chain (id 4663).
3
+ Build on Hookr, the Uniswap v4 hook on Robinhood Chain: launch tokens with Hook Blocks, open pools, quote and swap.
5
4
 
6
- Two roots are live. A pool names exactly one of them in `PoolKey.hooks`:
5
+ The package carries Hookr's contracts, release id `hookr-1`: their addresses, deployment and ABIs, the 22-quote catalog, the bounds of every setting of the core Hook Blocks and advisories, and the builders hookr.fun itself uses. It holds no wallet and signs nothing. Each builder returns `{ chainId, to, data, value }` for your wallet to send.
7
6
 
8
- | Root | Export | What it runs |
9
- | --- | --- | --- |
10
- | `HookrModularHookV6` | `ADDRESSES.root`, `ROOTS.default` | the five native rules |
11
- | `HookrModularHookV6WthV5` | `ADDRESSES.recaptureRoot`, `ROOTS.recapture` | the same five rules plus the arbitrage-recapture lane |
12
-
13
- Earlier recapture roots still hold trading pools. Their pools read back like any other, so resolve
14
- a root from the pool's own `PoolKey.hooks` or from the registry rather than from a list.
7
+ - [Install](#install)
8
+ - [Quick start](#quick-start)
9
+ - [Addresses](#addresses)
10
+ - [Wallet Cap](#wallet-cap)
11
+ - [Arb recapture](#arb-recapture)
12
+ - [Gas](#gas)
13
+ - [Quotes](#quotes)
14
+ - [What is in the package](#what-is-in-the-package)
15
15
 
16
16
  ## Install
17
17
 
@@ -19,135 +19,338 @@ a root from the pool's own `PoolKey.hooks` or from the registry rather than from
19
19
  npm install hookr-sdk viem
20
20
  ```
21
21
 
22
- ## RPC
22
+ ESM only, with TypeScript types. Node 18.16 or newer and viem 2. `ROBINHOOD_CHAIN` is the chain for viem, with RobinScan as its explorer. On viem 2.55 or newer, `robinhood` from `viem/chains` is the same chain with Blockscout as its explorer.
23
+
24
+ ## Quick start
25
+
26
+ ### Set up
23
27
 
24
- `RPC_URL` is the chain's public, keyless endpoint. It is fine for a few reads. Under load it rate
25
- limits, and a wide `eth_getLogs` can come back as an empty array instead of an error. `listMarkets`
26
- scans in windows of `MARKET_SCAN_CHUNK_BLOCKS` (3,000,000 blocks, the width the app's own scanners
27
- use; pass `chunkBlocks` to lower it) so no single answer is wide, but anything that must not miss
28
- data should still run on a provider endpoint you own:
28
+ Every builder takes the release's manifest: each contract it calls, at its address, with its runtime code hash. `readReleaseManifest` reads that code from chain and refuses it unless every contract runs the code the release deployed.
29
29
 
30
30
  ```ts
31
- import { http } from "viem";
32
- import { RPC_URL } from "hookr-sdk";
31
+ import "viem/window";
32
+ import { createPublicClient, createWalletClient, custom, http } from "viem";
33
+ import { HOOKR_1, ROBINHOOD_CHAIN, readReleaseManifest } from "hookr-sdk";
33
34
 
34
- const transport = http(process.env.HOOKR_RPC_URL ?? RPC_URL);
35
+ const client = createPublicClient({ chain: ROBINHOOD_CHAIN, transport: http() });
36
+ const wallet = createWalletClient({ chain: ROBINHOOD_CHAIN, transport: custom(window.ethereum!) });
37
+ const [account] = await wallet.getAddresses();
38
+
39
+ const manifest = await readReleaseManifest(client, HOOKR_1);
35
40
  ```
36
41
 
37
- ## List and validate rules
42
+ `releaseManifest(HOOKR_1)` builds the same manifest from the package alone, with no reads.
43
+
44
+ `http()` uses the chain's public node. It is fine for a few reads. Anything that scans logs or must not miss data should use a provider endpoint of its own.
45
+
46
+ ### Read a pool
47
+
48
+ A pool on Hookr's shared hook is a Uniswap v4 pool whose `PoolKey.hooks` is `HookrRoot`. A hook you deploy through the owned-root factory is an unmodified copy of it at an address of its own, and its pools name that address instead. Either way the pool's fee is `DYNAMIC_FEE`: the hook sets the LP fee on every swap.
38
49
 
39
50
  ```ts
40
- import { RULES, PARAMS, defaultBlock, validateStack } from "hookr-sdk";
51
+ import { DYNAMIC_FEE, HOOKR_1, ZERO, laneRootAbi, poolId, rootAbi, rulesAbi, type PoolKey } from "hookr-sdk";
52
+
53
+ const key: PoolKey = {
54
+ currency0: ZERO, // native ETH sorts first
55
+ currency1: tokenAddress,
56
+ fee: DYNAMIC_FEE,
57
+ tickSpacing: 60,
58
+ hooks: HOOKR_1.contracts.HookrRoot,
59
+ };
60
+ const id = poolId(key);
61
+
62
+ const config = await client.readContract({ address: HOOKR_1.contracts.HookrRoot, abi: rootAbi, functionName: "poolConfig", args: [id] });
63
+ const blocks = await client.readContract({ address: config.rules, abi: rulesAbi, functionName: "config", args: [id] });
64
+ const [executor, codeHash, partnerBps, family, laneOn, gasCap, gasFloor] = await client.readContract({
65
+ address: HOOKR_1.contracts.HookrRoot,
66
+ abi: laneRootAbi,
67
+ functionName: "laneOf",
68
+ args: [id],
69
+ });
70
+ ```
71
+
72
+ `config` is the pool's `PoolConfig`: its quote, base LP fee and caps, and the HookrRules contract that holds its Hook Blocks, one of the release's [Rules modules](#rules-modules). `blocks` is those Hook Blocks as frozen at launch: Anti-Snipe, the dynamic fee, Auto Burn, LP Rewards, the royalty and the protocol share. `laneOn` says whether arb recapture runs on the pool, and `gasFloor` is the least gas a swap must carry while it does ([Gas](#gas)). On the Wallet Cap Rules, `blocks.potMinBuyQuote` is the pool's [Wallet Cap](#wallet-cap).
73
+
74
+ ### Quote
41
75
 
42
- console.log(RULES.map((r) => r.key)); // ["snipe", "fee", "burn", "lp", "pot"]
76
+ `HookrQuoter` simulates the swap through the pool's own callbacks, so the quote is what the swap pays and gets, fees and Hook Blocks included. Send it as an `eth_call` from the real payer.
43
77
 
44
- const stack = [defaultBlock("fee"), defaultBlock("lp")];
45
- const { ok, errors } = validateStack(stack);
78
+ ```ts
79
+ import { decodeFunctionResult, parseEther } from "viem";
80
+ import { MIN_SQRT_PRICE_LIMIT, buildQuote, quoterAbi } from "hookr-sdk";
81
+
82
+ const swap = {
83
+ key,
84
+ zeroForOne: true, // ETH in, token out
85
+ amountSpecified: -parseEther("0.1"), // negative: exact input
86
+ amountBound: 1n, // the least out, set from the quote below
87
+ sqrtPriceLimitX96: MIN_SQRT_PRICE_LIMIT, // fill in full; MAX_SQRT_PRICE_LIMIT the other way
88
+ recipient: account,
89
+ deadline: BigInt(Math.floor(Date.now() / 1000) + 600), // at most an hour ahead
90
+ };
91
+
92
+ const quote = buildQuote(manifest, swap, account);
93
+ const { data } = await client.call({ account, to: quote.to, data: quote.data });
94
+ const receipt = decodeFunctionResult({ abi: quoterAbi, functionName: "quote", data: data! });
95
+ // receipt.outputAmount, receipt.quoteFee, receipt.subjectBurn, receipt.lpFeePips
46
96
  ```
47
97
 
48
- ## Build and send a create-market call
98
+ ### Swap
49
99
 
50
- A market opens on the default root unless `root: ROOTS.recapture` is passed. On the recapture
51
- root the builder also freezes the five correction fields of the stack (`RECAPTURE_CORRECTION`
52
- plus the creator), without which the pool would carry that root's name and never correct. The
53
- registry admits that lane on native-ETH-quoted pools only, so the builder throws on any other
54
- quote, and it needs `creator` (the launching wallet) to freeze as the correction creator.
100
+ `buildSwap` calls `HookrRouter.swap`. Native ETH in rides as the call's value. An ERC-20 in needs an allowance to the router first: `swapAllowance` says how much, `buildApprovals` builds the calls.
55
101
 
56
102
  ```ts
57
- import { createWalletClient, http } from "viem";
58
- import { CHAIN_ID, NATIVE_ADDRESS, ROOTS, buildExistingTokenMarketCall } from "hookr-sdk";
103
+ import { buildSwap } from "hookr-sdk";
59
104
 
60
- const call = buildExistingTokenMarketCall({
61
- subject: token, quote: usdcAddress, tickSpacing: 60, sqrtPriceX96,
62
- rules: { baseFeePips: 3_000, blocks: stack },
63
- protocolRecipient, protocolShareBps, caps, // read from the coordinator and native block first
64
- });
65
- const hash = await walletClient.writeContract(call);
66
-
67
- // The same market on the recapture root: native ETH quote, and the creator wallet.
68
- const recaptureCall = buildExistingTokenMarketCall({
69
- subject: token, quote: NATIVE_ADDRESS, tickSpacing: 60, sqrtPriceX96,
70
- rules: { baseFeePips: 3_000, blocks: stack },
71
- protocolRecipient, protocolShareBps, caps,
72
- root: ROOTS.recapture, creator: account.address,
73
- });
105
+ const minOut = (receipt.outputAmount * 995n) / 1000n; // 0.5% slippage
106
+ const swapCall = buildSwap(manifest, { ...swap, amountBound: minOut });
107
+
108
+ const estimate = await client.estimateGas({ account, to: swapCall.to, data: swapCall.data, value: swapCall.value });
109
+ const hash = await wallet.sendTransaction({ account, to: swapCall.to, data: swapCall.data, value: swapCall.value, gas: estimate + estimate / 6n });
74
110
  ```
75
111
 
76
- `caps` comes from `nativeMechanicsBlock.validateStack(...)`; `protocolShareBps` from
77
- `marketCoordinator.protocolShareBps(creator)`. See `market.ts`'s module doc for what this
78
- package computes and what it takes as an already-resolved input.
112
+ When the estimate fails, the swap would revert as built. Check the quote, the allowance and the deadline before you try again.
113
+
114
+ ### Launch a token with Hook Blocks
79
115
 
80
- ## Read a market, quote, and swap
116
+ One launch creates the token and opens its pools, each with its own Hook Blocks, frozen when the pool opens. `defaultRulesLaunch` sets every block to its default. Change what you want, and `rulesPoolTerms` checks every value against the contracts' bounds and returns the pool's caps and gas.
81
117
 
82
- `poolKeyFor` defaults `hooks` to the default root. Pass `hooks: ADDRESSES.recaptureRoot` for a
83
- pool on the recapture root, or use the `kernel` a `readMarket` row carries.
118
+ Anti-Snipe's launch guard, on by default, counts the blocks the contracts read as `block.number`: Ethereum's, which each Robinhood Chain block carries as `l1BlockNumber`. `readParentBlockNumber` reads it. `getBlockNumber()` is the chain's own, far larger number, and a guard counted from it is one the Rules refuse.
84
119
 
85
120
  ```ts
86
- import { createPublicClient, http } from "viem";
87
- import { ADDRESSES, readMarket, poolKeyFor, quoteExactInput, buildExactInputSwapCall } from "hookr-sdk";
88
-
89
- const client = createPublicClient({ transport });
90
- const market = await readMarket(client, poolId);
91
- const key = poolKeyFor({
92
- subject: market.subject, quote: market.quote, tickSpacing: market.tickSpacing,
93
- hooks: market.kernel, // ADDRESSES.root or ADDRESSES.recaptureRoot
94
- });
95
- const { amountOut } = await quoteExactInput(client, { key, user, currencyIn: market.quote, amountIn });
96
- const swapCall = buildExactInputSwapCall({ key, currencyIn: market.quote, amountIn, amountOutMinimum, recipient: user, deadline });
121
+ import { zeroHash } from "viem";
122
+ import {
123
+ ARTIFACT_ABIS,
124
+ HOOKR_1,
125
+ RECAPTURE_OFF,
126
+ ZERO,
127
+ buildLaunch,
128
+ defaultRulesLaunch,
129
+ launchFeeAbi,
130
+ readParentBlockNumber,
131
+ rulesAbi,
132
+ rulesPoolTerms,
133
+ type Admission,
134
+ } from "hookr-sdk";
135
+
136
+ const { HookrRegistry, HookrRoot, HookrRules, HookrLauncher } = HOOKR_1.contracts;
137
+ const blockNumber = await readParentBlockNumber(client);
138
+ const [open, admission, minProtocolShareBps, [launchFee]] = await Promise.all([
139
+ client.readContract({ address: HookrRegistry, abi: ARTIFACT_ABIS.HookrRegistry, functionName: "rootOpenFor", args: [HookrRoot, account] }),
140
+ client.readContract({ address: HookrRegistry, abi: ARTIFACT_ABIS.HookrRegistry, functionName: "admission", args: [HookrRoot, HookrRules] }),
141
+ client.readContract({ address: HookrRules, abi: rulesAbi, functionName: "minProtocolShareBps" }),
142
+ client.readContract({ address: HookrLauncher, abi: launchFeeAbi, functionName: "launchFee" }),
143
+ ]);
144
+ if (!open) throw new Error("The root takes no launch from this wallet now");
145
+
146
+ // Your opening price and range. ETH is currency0, so amount0Max is the ETH you seed.
147
+ const seed = { sqrtPriceX96, tickLower, tickUpper, liquidity, amount0Max, amount1Max };
148
+ const ctx = {
149
+ blockNumber,
150
+ minProtocolShareBps,
151
+ launchLiquidity: seed.liquidity,
152
+ launchSqrtPriceX96: seed.sqrtPriceX96,
153
+ quoteIsCurrency0: true,
154
+ liquidityOwner: HookrLauncher,
155
+ };
156
+
157
+ const settings = defaultRulesLaunch(ctx);
158
+ settings.rules.burnBps = 50; // Auto Burn, in basis points
159
+ settings.rules.lpBps = 300; // LP Rewards, in basis points
160
+ const terms = rulesPoolTerms(settings, ctx, admission as Admission);
161
+
162
+ const member = {
163
+ quote: ZERO,
164
+ tickSpacing: settings.tickSpacing,
165
+ ...seed,
166
+ config: {
167
+ subject: ZERO, // the launcher fills in the token
168
+ quote: ZERO,
169
+ rules: HookrRules,
170
+ advisory: ZERO,
171
+ liquidityOwner: HookrLauncher,
172
+ baseLpFeePips: settings.baseLpFeePips,
173
+ caps: terms.caps,
174
+ rulesGasLimit: terms.rulesGasLimit,
175
+ advisoryGasLimit: terms.advisoryGasLimit,
176
+ advisoryPhases: terms.advisoryPhases,
177
+ advisoryFailOpen: false,
178
+ policyId: zeroHash,
179
+ },
180
+ rules: settings.rules,
181
+ recapture: RECAPTURE_OFF,
182
+ };
183
+
184
+ const token = { existing: ZERO, name: "Hook", symbol: "HOOK", supply: 10n ** 27n, salt };
185
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + 600);
186
+ const launchCall = buildLaunch(manifest, token, HookrRoot, [member], seed.amount0Max, deadline, launchFee);
187
+ await wallet.sendTransaction({ account, to: launchCall.to, data: launchCall.data, value: launchCall.value });
97
188
  ```
98
189
 
99
- A swap on a recapture pool needs more gas than an estimate reports, because the correction is
100
- fail-open: an estimate converges on the cheapest swap that succeeds, which is the one that skipped
101
- the correction. The swap builders put `gas: CORRECTION_SWAP_GAS_FLOOR` (1,100,000, the app's
102
- floor) on the call whenever the pool's root is the current recapture root, so pass the call to
103
- `writeContract` as it is; on the default root the call carries no `gas` and the wallet estimates
104
- as usual. A pool opened on a superseded recapture root keeps its executor and keeps correcting;
105
- read `poolCorrects(await readPoolRules(client, poolId))` and pass it as `correcting` to the swap
106
- builders to put the same floor on that call (since 0.2.1).
190
+ One launch opens one to eight pools of the same token, each on its own quote: pass more members. `buildLaunchAdvised` binds an advisory per pool, and `buildLaunchFamily` adds a dev buy, supply weights and an opening price check. `HookrLauncher.predictToken(creator, token)` gives the token's address before you send.
191
+
192
+ A member with arb recapture on takes the release's default `RecaptureConfig` in place of `RECAPTURE_OFF`. Read it with `recaptureAbi` at the HookrRules address: `recaptureDefaults()` belongs to HookrRecapture, which HookrRules reaches for every call it does not handle itself.
193
+
194
+ ```ts
195
+ import { recaptureAbi } from "hookr-sdk";
196
+
197
+ const [recapture] = await client.readContract({ address: HookrRules, abi: recaptureAbi, functionName: "recaptureDefaults" });
198
+ const recaptureMember = { ...member, recapture };
199
+ ```
107
200
 
108
- ## Roots
201
+ ### Open pools for an existing token
109
202
 
110
- `ROOTS` names the two roots Hookr opens markets on today. The registry itself never retires a
111
- root, so `listRoots` shows every kernel it has registered, current or not, with its status and
112
- whether its profile is sealed (the coordinator opens markets on sealed profiles only):
203
+ The same launch, with the token named and nothing to mint. The launcher pulls the token side of each pool, so approve it first: `launchFunding` lists every allowance the launch needs.
113
204
 
114
205
  ```ts
115
- import { listRoots } from "hookr-sdk";
206
+ import { buildApprovals, launchFunding } from "hookr-sdk";
116
207
 
117
- for (const root of await listRoots(client)) {
118
- console.log(root.hook, root.status, root.sealed ? "sealed" : "unsealed");
119
- }
208
+ const existing = { existing: tokenAddress, name: "", symbol: "", supply: 0n, salt: zeroHash };
209
+ const funding = launchFunding(manifest, tokenAddress, true, [member], launchFee);
210
+ for (const allowance of funding.allowances)
211
+ for (const approve of buildApprovals(manifest, allowance, currentAllowance))
212
+ await wallet.sendTransaction({ account, to: approve.to, data: approve.data });
213
+
214
+ const openCall = buildLaunch(manifest, existing, HookrRoot, [member], seed.amount0Max, deadline, launchFee);
120
215
  ```
121
216
 
122
- Pools on any of those roots read, quote and swap through this package when their root's address is
123
- passed as `hooks`; only market creation is limited to `ROOTS`.
217
+ `HookrLauncher.memberKey(subject, quote, tickSpacing, root)` gives each pool's `PoolKey` and whether it is still free.
124
218
 
125
- ## Claim
219
+ ### Claim
220
+
221
+ Royalties, integrator fees and a trader's arb recapture rebate are held per currency in each pool's own Rules, the `config.rules` its `poolConfig` names (see [Read a pool](#read-a-pool)), until claimed.
126
222
 
127
223
  ```ts
128
- import { claimableCall, claimCall } from "hookr-sdk";
224
+ import { rulesAbi } from "hookr-sdk";
129
225
 
130
- const owed = await client.readContract(claimableCall(quoteAddress, account));
131
- if (owed > 0n) await walletClient.writeContract(claimCall(quoteAddress));
226
+ const owed = await client.readContract({ address: config.rules, abi: rulesAbi, functionName: "claimable", args: [currency, account] });
227
+ if (owed > 0n)
228
+ await wallet.writeContract({ account, address: config.rules, abi: rulesAbi, functionName: "claim", args: [currency] });
132
229
  ```
133
230
 
134
- ## Rule catalog
135
-
136
- `RULES` and `PARAMS` are this package's own machine-readable list of the five public rules and
137
- their bounds, and the same list is served live at https://hookr.fun/api/hooks/catalog together
138
- with the roots and contract addresses, the fee model, the composition limits and prefilled
139
- builder links for example configurations. Both come from the same source in the Hookr app, and a
140
- parity test in that repository keeps the package and the app in step.
141
-
142
- ## Changes
143
-
144
- - 0.2.1: the swap builders take `correcting` (default `isCorrectingRoot(key.hooks)`, the current
145
- recapture root), and `poolCorrects(rules)` reads from a pool's frozen rules whether it names a
146
- correction executor, so a pool on a superseded recapture root can carry the gas floor too.
147
- - 0.2.0: names both live roots (`ADDRESSES.recaptureRoot`, `RECAPTURE_KERNEL_ID`, `ROOTS`); the
148
- market builders take `root` and freeze the recapture correction fields (`RECAPTURE_CORRECTION`);
149
- swap calls carry `gas: CORRECTION_SWAP_GAS_FLOOR` on a correcting root; `listMarkets` scans in
150
- windows (`MARKET_SCAN_CHUNK_BLOCKS`, `chunkBlocks`); `listRoots` reads every registered root
151
- from the registry; documents the public RPC's limits. The README on npm for 0.1.0 had drifted
152
- from the repository.
153
- - 0.1.0: first release, default root only.
231
+ `claimTo` sends the claim to another address. `claimAsClaims` takes it as Uniswap v4 ERC-6909 claims.
232
+
233
+ ## Addresses
234
+
235
+ Robinhood Chain, chain id 4663. `HOOKR_1.contracts` holds every address. `HOOKR_1.deployment` holds the deployment: the block the manifest starts from and its hash, each contract's runtime code hash, and the hash of the release's source verification on Blockscout. Each address below opens on RobinScan, where its source is verified.
236
+
237
+ | Contract | `hookr-1` | What it does |
238
+ | --- | --- | --- |
239
+ | HookrRoot | [`0x89c9FB50d1f03192230FFB1c279BB6E7d3Fe6aCc`](https://robin.etherscan.io/address/0x89c9FB50d1f03192230FFB1c279BB6E7d3Fe6aCc) | The hook each pool names in `PoolKey.hooks` |
240
+ | HookrRouter | [`0xC2Bc9a3fe6c4Ce8C2F7EB4655b0869b01D8c93C9`](https://robin.etherscan.io/address/0xC2Bc9a3fe6c4Ce8C2F7EB4655b0869b01D8c93C9) | Swaps through a Hookr pool |
241
+ | HookrQuoter | [`0x445D7891381Aa7F91F5048EdBC6a611ccBDe7865`](https://robin.etherscan.io/address/0x445D7891381Aa7F91F5048EdBC6a611ccBDe7865) | Quotes a swap with the pool's own callbacks |
242
+ | HookrLauncher | [`0x1cf2748A6670572425eAbf9Ab9e5BC4eb2eB8Ef4`](https://robin.etherscan.io/address/0x1cf2748A6670572425eAbf9Ab9e5BC4eb2eB8Ef4) | Opens a token's pools in one launch |
243
+ | HookrRules | [`0x4a71c270b1520F94064268102AebD744b0DE7942`](https://robin.etherscan.io/address/0x4a71c270b1520F94064268102AebD744b0DE7942) | Holds each pool's Hook Blocks and pays its fees |
244
+ | HookrRegistry | [`0x463c82beA1a52F4Cc05ca0741a24F0AD24bA4602`](https://robin.etherscan.io/address/0x463c82beA1a52F4Cc05ca0741a24F0AD24bA4602) | Admits roots, Hook Blocks, advisories and quotes, behind the owner's timelock |
245
+ | HookrTreasury | [`0x2242F7FC75606500503075f29ea8cDA684EfB4a9`](https://robin.etherscan.io/address/0x2242F7FC75606500503075f29ea8cDA684EfB4a9) | Holds Hookr's share and the integrator list |
246
+ | HookrForwarder | [`0xe7863CC0E2Fb783b44c279aB159bcD812c2ceA1c`](https://robin.etherscan.io/address/0xe7863CC0E2Fb783b44c279aB159bcD812c2ceA1c) | Runs a signed swap for a relayer |
247
+ | PoolManager | [`0x8366a39CC670B4001A1121B8F6A443A643e40951`](https://robin.etherscan.io/address/0x8366a39CC670B4001A1121B8F6A443A643e40951) | Uniswap v4's PoolManager, `POOL_MANAGER` |
248
+
249
+ Every other contract is in `HOOKR_1.contracts`.
250
+
251
+ ### Rules modules
252
+
253
+ A pool binds one HookrRules, its `PoolConfig.rules`, and keeps it. Beside the release's own, the registry admits three more on `HookrRoot` on the same terms, each with the HookrRecapture its constructor created. `HOOKR_1.rulesModules` holds all four and `rulesModule(HOOKR_1, name)` names one. The manifest pins each by its runtime code hash, so `readReleaseManifest` refuses one that runs other code.
254
+
255
+ | Name | Rules | HookrRecapture | What a pool on them runs |
256
+ | --- | --- | --- | --- |
257
+ | `standard` | [`0x4a71c270b1520F94064268102AebD744b0DE7942`](https://robin.etherscan.io/address/0x4a71c270b1520F94064268102AebD744b0DE7942) | [`0xF83569E564d70daebc35950f66a73f2e2B915583`](https://robin.etherscan.io/address/0xF83569E564d70daebc35950f66a73f2e2B915583) | The release's HookrRules |
258
+ | `whole-pot` | [`0x10f684361b35C8411a2c07F68A6fB1F4B7e555F7`](https://robin.etherscan.io/address/0x10f684361b35C8411a2c07F68A6fB1F4B7e555F7) | [`0x60Efc4a41498e9433be2155b6cc1677AEC31D649`](https://robin.etherscan.io/address/0x60Efc4a41498e9433be2155b6cc1677AEC31D649) | The same Rules, where King of the Pool's winner takes the whole pot |
259
+ | `wallet-cap` | [`0xb67b73E28C522a3AAC438bE970bf94976Deee52a`](https://robin.etherscan.io/address/0xb67b73E28C522a3AAC438bE970bf94976Deee52a) | [`0xbbC43DC2f01F5E7882caCd78EF094A44eF0E4d23`](https://robin.etherscan.io/address/0xbbC43DC2f01F5E7882caCd78EF094A44eF0E4d23) | The whole-pot Rules plus [Wallet Cap](#wallet-cap) |
260
+ | `round-trip` | [`0x3d7e31746127FD9a14d3C7fC12E73611D2e8cb34`](https://robin.etherscan.io/address/0x3d7e31746127FD9a14d3C7fC12E73611D2e8cb34) | [`0xddFF6326E5CB9AA98b88e9064d9A9570f777f8A3`](https://robin.etherscan.io/address/0xddFF6326E5CB9AA98b88e9064d9A9570f777f8A3) | Rules that keep a record of who traded in each block, for Buy/Sell Block's round-trip check |
261
+
262
+ ## Wallet Cap
263
+
264
+ While a launch's guard lasts, no wallet can buy more than the pool's cap of the token, its `rules.potMinBuyQuote`. A pool takes it at launch by binding the `wallet-cap` Rules.
265
+
266
+ ```ts
267
+ import { checkWalletCapLaunch, readWalletCap, rulesModule, walletCapAmount, walletCapPoolTerms, walletCapRevert } from "hookr-sdk";
268
+
269
+ const walletCap = rulesModule(HOOKR_1, "wallet-cap");
270
+ const capAdmission = await client.readContract({ address: HookrRegistry, abi: ARTIFACT_ABIS.HookrRegistry, functionName: "admission", args: [HookrRoot, walletCap.rules] });
271
+
272
+ // The defaults keep the launch guard on: 50 parent blocks, about 10 minutes. Until it ends, no wallet buys more than 2% of the supply.
273
+ const capped = defaultRulesLaunch(ctx);
274
+ capped.rules.potMinBuyQuote = walletCapAmount(token.supply, 200);
275
+ const cappedTerms = walletCapPoolTerms(capped, ctx, capAdmission as Admission);
276
+ const cappedMember = {
277
+ ...member,
278
+ config: { ...member.config, rules: walletCap.rules, caps: cappedTerms.caps, rulesGasLimit: cappedTerms.rulesGasLimit },
279
+ rules: capped.rules,
280
+ };
281
+ checkWalletCapLaunch([cappedMember]);
282
+
283
+ // A pool binds only while its guard has yet to end, so the launch must land well inside it.
284
+ const cappedDeadline = BigInt(Math.floor(Date.now() / 1000) + 300);
285
+ const cappedCall = buildLaunch(manifest, token, HookrRoot, [cappedMember], seed.amount0Max, cappedDeadline, launchFee);
286
+
287
+ // Once it trades: what a wallet may still buy, until the guard ends at a parent block (readParentBlockNumber).
288
+ const cap = await readWalletCap(client, { poolId: id, wallet: account, subject: tokenAddress });
289
+ if (cap) console.log(`${cap.room} of ${cap.cap} left until parent block ${cap.guardEndBlock}`);
290
+
291
+ // A buy past the cap fails its quote and its gas estimate with GuardWalletLimit, however the contracts wrapped it.
292
+ try {
293
+ await client.estimateGas({ account, to: swapCall.to, data: swapCall.data, value: swapCall.value });
294
+ } catch (error) {
295
+ console.log(walletCapRevert(error) ?? error);
296
+ }
297
+ ```
298
+
299
+ `readWalletCap` reads the pool through the Rules' `extsload`, one storage word at a time, and gives null for a pool on any other Rules. `walletCapPoolSlot` and `walletCapBoughtSlot` give the slots for reads of your own, and `WALLET_CAP_ABI` is what the Wallet Cap Rules add to `rulesAbi`.
300
+
301
+ ## Arb recapture
302
+
303
+ A pool with arb recapture on lets What The Hook's executor close the price gap between the pool and outside venues inside the trader's own swap, and pays the profit out:
304
+
305
+ | Who | Share of each profit |
306
+ | --- | --- |
307
+ | Hookr | 25% (`RECAPTURE_PROTOCOL_BPS`, 2,500) |
308
+ | What The Hook | up to 25% (`MAX_LANE_PARTNER_BPS`, 2,500), what its executor keeps |
309
+ | The pool | the rest: the trader's rebate (0 to 50% of it, 25% by default), the King of the Pool pot (off by default; up to 50% of it on the standard and round-trip Rules, up to all of it on the whole-pot and Wallet Cap Rules) and the LPs |
310
+
311
+ The pool's split is frozen at launch. `recaptureSplit` and `recaptureProfit` work it out to the wei; `recaptureSplit` takes a pot of up to 50%. The SDK's builders always open a pool with the pot off. The LPs' part is released over `releaseBlocks` L2 blocks: 1,200 to 864,000, 36,000 by default. The release counts at most 10 of them a second, so 36,000 blocks take at least an hour.
312
+
313
+ The lane's switch is not frozen: the registry's owner, or its guardian once one is appointed, can switch the lane off, or lower its gas cap, for every pool that froze it, at once, and the swaps then skip or shrink arb recapture. Switching it back on, or raising the cap, waits in the registry's 30-minute timelock. The switch touches nothing else of the pool.
314
+
315
+ ## Gas
316
+
317
+ A swap on a pool with arb recapture on must carry the lane's gas floor, or it reverts `InsufficientLaneGas`. The floor is `2 × gasCap + gasCap / 63 + 755,000`: `laneGasFloor(gasCap)` here, `gasFloor` from `laneOf(id)` on chain. At the lane's 3,000,000 gas cap it is 6,802,619. The swap's own gas comes on top: a buy through `HookrRouter` on a live lane pool estimates at about 7.5 million.
318
+
319
+ Estimate and add a sixth, as the [Swap](#swap) example does. The estimate already includes the floor, which is why it is high. If your wallet sets a fixed limit, use at least 8.8 million on such a pool. A wallet pays for the gas used, not the limit. A pool without arb recapture has no floor.
320
+
321
+ ## Quotes
322
+
323
+ A pool may quote in native ETH, any of the 22 tokens on the genesis catalog, any Robinhood stock token, and any other token the registry admits. `ROBINHOOD_QUOTE_CATALOG` is the catalog in the registry's order, each entry read from chain at block 81,225,166: address, symbol, decimals and runtime code hash. `classifyQuote` labels any address, `quoteWarnings` says what to tell a user, and `quoteAdmission` asks the registry whether a quote is admitted right now.
324
+
325
+ ## What is in the package
326
+
327
+ | Source file | What it does |
328
+ | --- | --- |
329
+ | `releases.ts` | `HOOKR_1`: addresses, deployment, the Rules modules, the manifest, `ROBINHOOD_CHAIN`, explorer links, the lane gas floor and the parent block the launch guard counts |
330
+ | `wallet-cap.ts` | Wallet Cap: the cap from a share of supply, a pool's terms and a launch's checks, the reads through `extsload`, and its refusal in plain words |
331
+ | `wallet.ts` | The launcher, router and quoter calls, each checked against its bounds |
332
+ | `launch.ts` | Multi-pool launch, the dev buy and exactly what the launcher pulls |
333
+ | `params.ts` | Every setting of the core Hook Blocks and advisories, with its range and default, and the checks that refuse a value outside them |
334
+ | `rules.ts` | Encodes and checks a pool's Hook Blocks, and prices the Snipe tax, the dynamic fee and the arb recapture split |
335
+ | `quotes.ts` | The quote catalog, quote classes and amounts in each quote's own decimals |
336
+ | `registry.ts` | Typed registry operations and the admission checks |
337
+ | `advisories.ts` | The managed fee and market-hours advisory configs |
338
+ | `abi.ts` | The call ABIs for the root and its lane, the router, quoter, launcher, HookrRules, HookrRecapture, the registry, treasury, programs, referrals and forwarder |
339
+ | `generated.ts` | `ARTIFACT_ABIS`, the full ABIs of the registry, HookrRules, the treasury, the root factory and pair root, the fee and session advisories, the liquidity vault, the family contracts, LP Boost and the owned-root contracts, and the constants the encoders use |
340
+ | `vault.ts`, `referrals.ts`, `proofs.ts` | Liquidity vault and treasury calls, referral authorizations and reward proofs |
341
+ | `client.ts` | `HookrClient`, for a Hookr service at a URL you give it. Hookr runs no public service. |
342
+
343
+ Everything but `releases.ts`, `deployments.ts` and `wallet-cap.ts` is the release SDK hookr.fun runs, copied byte for byte. Its quote-admission exports in `registry.ts` are deprecated: HookrRegistry does not have their calls. `provenance.json` names the release's source commit and the hash of every file. Every contract of the release has its source verified at its address on RobinScan and Sourcify.
344
+
345
+ Before you send a call, check the wallet's chain, the payer, the token approvals, the deadline and the slippage yourself. A quote simulates the pool's accounting. It does not prove approvals, balances or gas. Treat token names and links as untrusted display text.
346
+
347
+ ## Links
348
+
349
+ - Docs: [hookr.fun/docs](https://hookr.fun/docs)
350
+ - Explorer: [RobinScan](https://robin.etherscan.io)
351
+ - Moving from 0.2.x: [MIGRATING.md](https://unpkg.com/hookr-sdk@1/MIGRATING.md), in the package
352
+ - Changes: [CHANGELOG.md](https://unpkg.com/hookr-sdk@1/CHANGELOG.md), in the package
353
+
354
+ ## License
355
+
356
+ MIT