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/dist/rules.js DELETED
@@ -1,296 +0,0 @@
1
- /**
2
- * The public rules a market's stack can carry, and their bounds.
3
- *
4
- * Copied from the Hookr app's `src/lib/hooks-model.ts`: `RULES` mirrors `HOOKS` reduced to
5
- * `{ key, name, summary, explanation }`, and `PARAMS` mirrors the bounds/step/default of the
6
- * app's `PARAMS[type]` (its display-only `fmt` function is not carried here; `unit` is a short,
7
- * human label instead — see the module doc below). Both are restricted to the app's
8
- * `PUBLIC_BUILDER_HOOK_ORDER`, which today is the five deployed blocks (`HOOK_ORDER`): the
9
- * directional tax module (`DIRECTIONAL_TAX_UI` in the app) is off and is not offered here either.
10
- * `src/test/hookr-sdk-parity.test.ts` in the app asserts the key order and every bound match.
11
- *
12
- * A rule compiles into the native block's `Config` (see `fees.ts` and `market.ts`); it is not a
13
- * separate contract call. At most five rules on a stack today, one per key, because there are
14
- * only five public rule keys — `validateStack` still checks the count explicitly so a future
15
- * sixth key does not silently raise the ceiling.
16
- */
17
- /** The public rule keys, in the order the app's builder palette offers them. */
18
- export const RULE_ORDER = ["snipe", "fee", "burn", "lp", "pot"];
19
- export const RULES = Object.freeze([
20
- Object.freeze({
21
- key: "snipe",
22
- name: "Anti-Snipe",
23
- summary: "Caps early buys and adds an LP fee in the first blocks",
24
- explanation: "It caps total ETH bought per pool block and adds an extra LP fee during a fixed opening window.",
25
- }),
26
- Object.freeze({
27
- key: "fee",
28
- name: "Surge Fees",
29
- summary: "Fee scales with trade size",
30
- explanation: "It raises the LP fee from a base rate toward a ceiling as trade size grows relative to in-range depth.",
31
- }),
32
- Object.freeze({
33
- key: "burn",
34
- name: "Auto Burn",
35
- summary: "Burns a share of actual buy output",
36
- explanation: "It sends a chosen share of each exact-input buy's token output directly to the dead address.",
37
- }),
38
- Object.freeze({
39
- key: "lp",
40
- name: "LP Rewards",
41
- summary: "Fee share streams to LPs",
42
- explanation: "It donates an ETH-side share of each exact-input buy to liquidity positions that are in range.",
43
- }),
44
- Object.freeze({
45
- key: "pot",
46
- name: "Nth-buy Pot",
47
- summary: "A public counter pays every Nth buy",
48
- explanation: "It fills a deterministic pot from exact-input buys and pays it to the recipient of every Nth qualifying buy.",
49
- }),
50
- ]);
51
- const RULES_BY_KEY = Object.freeze(Object.fromEntries(RULES.map((rule) => [rule.key, rule])));
52
- export function ruleFor(key) {
53
- return RULES_BY_KEY[key];
54
- }
55
- export const PARAMS = Object.freeze({
56
- snipe: Object.freeze([
57
- Object.freeze({
58
- key: "guard",
59
- label: "Guard window",
60
- min: 5,
61
- max: 500,
62
- step: 5,
63
- def: 100,
64
- unit: "BLK",
65
- hint: "Blocks after the pool opens with sniper protection active. Counted in the parent-chain blocks the contracts see (~12 s each), not this chain's ~0.1 s blocks, so 100 is roughly 20 minutes.",
66
- }),
67
- Object.freeze({
68
- key: "maxbuy",
69
- label: "Max buy",
70
- min: 0.1,
71
- max: 5,
72
- step: 0.1,
73
- def: 0.5,
74
- unit: "%",
75
- hint: "Cumulative cap on all buys in one pool block during the guard window, derived from a share of the launch's supply basis. Splitting one order does not create more room in that block.",
76
- }),
77
- Object.freeze({
78
- key: "tax",
79
- label: "Snipe tax",
80
- min: 0,
81
- max: 45,
82
- step: 5,
83
- def: 40,
84
- unit: "%",
85
- hint: "Extra LP fee on swaps inside the guard window. Goes to LPs.",
86
- }),
87
- ]),
88
- fee: Object.freeze([
89
- Object.freeze({
90
- key: "base",
91
- label: "Base fee",
92
- min: 0.05,
93
- max: 1,
94
- step: 0.05,
95
- def: 0.3,
96
- unit: "%",
97
- hint: "Swap fee when the pool is calm.",
98
- }),
99
- Object.freeze({
100
- key: "max",
101
- label: "Max fee",
102
- min: 1,
103
- max: 10,
104
- step: 0.5,
105
- def: 3,
106
- unit: "%",
107
- hint: "Ceiling for large trades relative to pool depth.",
108
- }),
109
- Object.freeze({
110
- key: "sens",
111
- label: "Size sensitivity",
112
- min: 1,
113
- max: 10,
114
- step: 1,
115
- def: 5,
116
- unit: "X",
117
- hint: "How fast the fee ramps as a trade eats the pool.",
118
- }),
119
- ]),
120
- burn: Object.freeze([
121
- Object.freeze({
122
- key: "cut",
123
- label: "Output burn",
124
- min: 0.1,
125
- max: 5,
126
- step: 0.1,
127
- def: 1,
128
- unit: "% OUT",
129
- hint: "Share of the actual token output sent directly to the dead address on each exact-input buy. No ETH vault or market buy.",
130
- }),
131
- ]),
132
- lp: Object.freeze([
133
- Object.freeze({
134
- key: "share",
135
- label: "LP fee share",
136
- min: 0.05,
137
- max: 1,
138
- step: 0.05,
139
- def: 0.25,
140
- unit: "%",
141
- hint: "Slice of each buy donated to in-range LPs.",
142
- }),
143
- ]),
144
- pot: Object.freeze([
145
- Object.freeze({
146
- key: "cut",
147
- label: "Pot share",
148
- min: 0.1,
149
- max: 2,
150
- step: 0.1,
151
- def: 0.5,
152
- unit: "% SWAP",
153
- hint: "Share of every exact-input buy added to the deterministic pot.",
154
- }),
155
- Object.freeze({
156
- key: "every",
157
- label: "Pays every N buys",
158
- min: 100,
159
- max: 5000,
160
- step: 100,
161
- def: 500,
162
- unit: "buys",
163
- hint: "The public counter advances at most once per pool per block. The Nth qualifying buy wins; this is not random.",
164
- }),
165
- Object.freeze({
166
- key: "minbuy",
167
- label: "Min qualifying buy",
168
- min: 0.001,
169
- max: 1,
170
- step: 0.001,
171
- def: 0.01,
172
- unit: "ETH",
173
- hint: "Buys below this cannot advance the public pot counter.",
174
- }),
175
- ]),
176
- });
177
- /** `eth` -> wei at fixed 9-decimal precision, matching the app's `toWei` (avoids float drift). */
178
- function toWei(eth) {
179
- return BigInt(Math.round(eth * 1e9)) * 10n ** 9n;
180
- }
181
- /**
182
- * Compile a stack of public rule blocks into the exact rate fields the native block's `Config`
183
- * carries, mirroring `compileStack` in the app's `src/lib/hooks-model.ts`.
184
- *
185
- * `baseFeePipsOverride` is the stack-level Base LP fee, chosen independently of whether a
186
- * Surge Fees rule is present — it always wins over the Surge block's own "Base fee" slider,
187
- * exactly like the app's builder. The block still supplies the surge ceiling and sensitivity.
188
- */
189
- export function compileRuleBlocks(blocks, baseFeePipsOverride) {
190
- const baseFeePips = Math.round(baseFeePipsOverride);
191
- const out = {
192
- guardBlocks: 0,
193
- maxBuyBps: 0,
194
- snipeTaxPips: 0,
195
- baseFeePips,
196
- maxFeePips: baseFeePips,
197
- surgeSens: 0,
198
- burnBps: 0,
199
- lpBps: 0,
200
- potBps: 0,
201
- potEveryNBuys: 0,
202
- potMinBuyWei: 0n,
203
- };
204
- for (const block of blocks) {
205
- const p = block.params;
206
- if (block.type === "snipe") {
207
- out.guardBlocks = Math.round(p.guard);
208
- out.maxBuyBps = Math.round(p.maxbuy * 100);
209
- out.snipeTaxPips = Math.round(p.tax * 10_000);
210
- }
211
- if (block.type === "fee") {
212
- out.maxFeePips = Math.round(p.max * 10_000);
213
- out.surgeSens = Math.round(p.sens);
214
- }
215
- if (block.type === "burn") {
216
- out.burnBps = Math.round(p.cut * 100);
217
- }
218
- if (block.type === "lp") {
219
- out.lpBps = Math.round(p.share * 100);
220
- }
221
- if (block.type === "pot") {
222
- out.potBps = Math.round(p.cut * 100);
223
- out.potEveryNBuys = Math.round(p.every);
224
- out.potMinBuyWei = toWei(p.minbuy);
225
- }
226
- }
227
- if (out.maxFeePips < out.baseFeePips)
228
- out.maxFeePips = out.baseFeePips;
229
- return Object.freeze(out);
230
- }
231
- /** The default single block for one rule: every param at its `def`. */
232
- export function defaultBlock(key) {
233
- const params = {};
234
- for (const spec of PARAMS[key])
235
- params[spec.key] = spec.def;
236
- return Object.freeze({ type: key, params: Object.freeze(params) });
237
- }
238
- /** The starting stack a new configuration opens with: no rules, base fee only. */
239
- export function defaultStack() {
240
- return Object.freeze([]);
241
- }
242
- export const MAX_STACK_RULES = 5;
243
- /** Whether `value` sits on `spec`'s step grid, within floating-point rounding. */
244
- function onStep(value, spec) {
245
- const steps = (value - spec.min) / spec.step;
246
- return Math.abs(steps - Math.round(steps)) < 1e-9;
247
- }
248
- /**
249
- * Validate a stack of rule blocks: unknown rule keys, duplicate rules, more than
250
- * `MAX_STACK_RULES` rules, an unknown parameter, or a parameter outside its bounds or off its
251
- * step grid. Every problem is reported, not just the first.
252
- */
253
- export function validateStack(blocks) {
254
- const errors = [];
255
- if (blocks.length > MAX_STACK_RULES) {
256
- errors.push(`A stack may carry at most ${MAX_STACK_RULES} rules; this one carries ${blocks.length}.`);
257
- }
258
- const seen = new Set();
259
- for (const block of blocks) {
260
- if (seen.has(block.type)) {
261
- errors.push(`Rule "${block.type}" appears more than once in the stack.`);
262
- continue;
263
- }
264
- seen.add(block.type);
265
- const specs = PARAMS[block.type];
266
- if (!specs) {
267
- errors.push(`Unknown rule "${block.type}".`);
268
- continue;
269
- }
270
- const knownKeys = new Set(specs.map((spec) => spec.key));
271
- for (const paramKey of Object.keys(block.params)) {
272
- if (!knownKeys.has(paramKey)) {
273
- errors.push(`Rule "${block.type}" has no parameter "${paramKey}".`);
274
- }
275
- }
276
- for (const spec of specs) {
277
- const value = block.params[spec.key];
278
- if (value === undefined) {
279
- errors.push(`Rule "${block.type}" is missing its "${spec.key}" parameter.`);
280
- continue;
281
- }
282
- if (!Number.isFinite(value)) {
283
- errors.push(`Rule "${block.type}"'s "${spec.key}" must be a finite number.`);
284
- continue;
285
- }
286
- if (value < spec.min || value > spec.max) {
287
- errors.push(`Rule "${block.type}"'s "${spec.key}" (${value}) must be between ${spec.min} and ${spec.max}.`);
288
- continue;
289
- }
290
- if (!onStep(value, spec)) {
291
- errors.push(`Rule "${block.type}"'s "${spec.key}" (${value}) must be a multiple of ${spec.step} from ${spec.min}.`);
292
- }
293
- }
294
- }
295
- return Object.freeze({ ok: errors.length === 0, errors: Object.freeze(errors) });
296
- }
package/dist/swap.d.ts DELETED
@@ -1,108 +0,0 @@
1
- /**
2
- * Quote and swap through Hookr's kernel router and quoter (chain 4663).
3
- *
4
- * Copied from the app's `src/lib/v2-swap.ts` (`v2ExactInputCall`, `v2ExactOutputCall`,
5
- * `v2QuoteCall`): empty module data, the full-fill price limit in both directions, and native
6
- * ETH sent as `value` exactly when the input currency is the zero address. A pool that carries
7
- * any input cut (surge, guard, burn, LP reward or pot) rejects any other limit outright — see
8
- * `market.ts`'s module doc for the fee model these cuts come from.
9
- */
10
- import type { Address, ContractFunctionArgs, Hex, PublicClient } from "viem";
11
- import { hookrKernelRouterV3Abi } from "./abi.js";
12
- import type { PoolKey } from "./market.js";
13
- /**
14
- * The gas limit a swap on a correcting root must carry (the app's `CORRECTION_SWAP_GAS_FLOOR` in
15
- * `src/lib/gas-headroom.ts`). The recapture root runs its correction in both swap phases, once in
16
- * `beforeSwap` and again in `afterSwap` (`HookrSwapKernelV5Wth.sol`, `_tryCorrection` at lines 214
17
- * and 250), each inside a bounded call that fails open: a wallet's gas estimate covers the swap
18
- * alone, so an estimated transaction succeeds with the correction skipped and nothing recaptured.
19
- * The swap builders below put this floor on the call whenever `key.hooks` is the current recapture
20
- * root, or whenever the caller passes `correcting: true`; pass it through to `writeContract` (viem
21
- * sends `gas` as the limit) instead of estimating.
22
- */
23
- export declare const CORRECTION_SWAP_GAS_FLOOR = 1100000n;
24
- /**
25
- * True when `hooks` is the current recapture root, which corrects in both swap phases. This is the
26
- * root's identity, not the pool's: a pool opened on a superseded recapture root keeps the executor
27
- * frozen into its rules and keeps correcting, and this returns false for it. Read that from the
28
- * pool itself with `poolCorrects(await readPoolRules(client, poolId))` and pass the answer as
29
- * `correcting` to the swap builders.
30
- */
31
- export declare function isCorrectingRoot(hooks: Address): boolean;
32
- /** The gas limit to send with a swap on `key`: the floor when the pool corrects (`correcting`,
33
- * defaulting to `isCorrectingRoot(key.hooks)`), else `undefined` (let the wallet estimate). */
34
- export declare function swapGasLimit(key: PoolKey, correcting?: boolean): bigint | undefined;
35
- /** Router / block `MIN_SQRT_PRICE_LIMIT`: the full-fill limit for a `zeroForOne` swap. */
36
- export declare const MIN_SQRT_PRICE_LIMIT = 4295128740n;
37
- /** Router / block `MAX_SQRT_PRICE_LIMIT`: the full-fill limit for a `oneForZero` swap. */
38
- export declare const MAX_SQRT_PRICE_LIMIT = 1461446703485210103287273052203988822378723970341n;
39
- /** `type(int128).max`: the router's and the quoter's ceiling on every amount and bound. */
40
- export declare const INT128_MAX: bigint;
41
- /** The bound an exact-input quote carries: the smallest value the quoter admits. */
42
- export declare const QUOTE_MIN_OUT_BOUND = 1n;
43
- /** The router's `moduleData` for a plain swap: the native block reads none. */
44
- export declare const EMPTY_MODULE_DATA: Hex;
45
- /** The full-fill `sqrtPriceLimitX96`: `MIN` when selling currency0, `MAX` otherwise. Any pool
46
- * with an input cut rejects every other limit on an exact-input buy. */
47
- export declare function fullFillLimit(zeroForOne: boolean): bigint;
48
- /** `zeroForOne = currencyIn == key.currency0`. Throws when `currencyIn` is not in the pool. */
49
- export declare function swapDirection(key: PoolKey, currencyIn: Address): boolean;
50
- export type RouterCall<F extends "exactInput" | "exactOutput"> = Readonly<{
51
- address: Address;
52
- abi: typeof hookrKernelRouterV3Abi;
53
- functionName: F;
54
- args: ContractFunctionArgs<typeof hookrKernelRouterV3Abi, "payable", F>;
55
- value: bigint;
56
- /** Set to `CORRECTION_SWAP_GAS_FLOOR` when the pool corrects; absent otherwise. */
57
- gas?: bigint;
58
- }>;
59
- /**
60
- * `exactInput` through the Hookr router. `value` is `amountIn` when `currencyIn` is native ETH,
61
- * else 0 (the router pulls the ERC-20 by `transferFrom`; approve it first). `gas` carries the
62
- * correction floor when the pool corrects; send it as the gas limit. `correcting` defaults to
63
- * `isCorrectingRoot(key.hooks)`; pass `poolCorrects(rules)` for a pool on a superseded root.
64
- */
65
- export declare function buildExactInputSwapCall(input: {
66
- key: PoolKey;
67
- currencyIn: Address;
68
- amountIn: bigint;
69
- amountOutMinimum: bigint;
70
- recipient: Address;
71
- deadline: bigint;
72
- correcting?: boolean;
73
- }): RouterCall<"exactInput">;
74
- /**
75
- * `exactOutput` through the Hookr router. `value` is `amountInMaximum` when `currencyIn` is
76
- * native ETH — the router refunds what the swap does not use. `correcting` works as on
77
- * `buildExactInputSwapCall`.
78
- */
79
- export declare function buildExactOutputSwapCall(input: {
80
- key: PoolKey;
81
- currencyIn: Address;
82
- amountOut: bigint;
83
- amountInMaximum: bigint;
84
- recipient: Address;
85
- deadline: bigint;
86
- correcting?: boolean;
87
- }): RouterCall<"exactOutput">;
88
- export type Quote = Readonly<{
89
- amountIn: bigint;
90
- amountOut: bigint;
91
- }>;
92
- /**
93
- * Quote through `kernelQuoter.quote`. Run as a simulated call from `account` rather than an
94
- * ordinary read: the function is declared `nonpayable` (it runs the full module loop, including
95
- * the pot leg, which is recipient-specific), which is also why the app's own equivalent
96
- * (`quoteV2` in `src/lib/v2-swap.ts`) calls `simulateContract` rather than `readContract` — the
97
- * same `eth_call` either way, just the viem entry point whose types match a nonpayable function.
98
- * `amountSpecified` is negative for an exact-input quote, bound at 1 wei/unit — the least the
99
- * quoter admits; pass `amountBound` to override it (an exact-output quote wants `INT128_MAX` or
100
- * your own maximum).
101
- */
102
- export declare function quoteExactInput(client: PublicClient, input: {
103
- key: PoolKey;
104
- user: Address;
105
- currencyIn: Address;
106
- amountIn: bigint;
107
- amountBound?: bigint;
108
- }): Promise<Quote>;
package/dist/swap.js DELETED
@@ -1,144 +0,0 @@
1
- import { hookrKernelQuoterV1Abi, hookrKernelRouterV3Abi } from "./abi.js";
2
- import { ADDRESSES, NATIVE_ADDRESS } from "./chain.js";
3
- /**
4
- * The gas limit a swap on a correcting root must carry (the app's `CORRECTION_SWAP_GAS_FLOOR` in
5
- * `src/lib/gas-headroom.ts`). The recapture root runs its correction in both swap phases, once in
6
- * `beforeSwap` and again in `afterSwap` (`HookrSwapKernelV5Wth.sol`, `_tryCorrection` at lines 214
7
- * and 250), each inside a bounded call that fails open: a wallet's gas estimate covers the swap
8
- * alone, so an estimated transaction succeeds with the correction skipped and nothing recaptured.
9
- * The swap builders below put this floor on the call whenever `key.hooks` is the current recapture
10
- * root, or whenever the caller passes `correcting: true`; pass it through to `writeContract` (viem
11
- * sends `gas` as the limit) instead of estimating.
12
- */
13
- export const CORRECTION_SWAP_GAS_FLOOR = 1100000n;
14
- /**
15
- * True when `hooks` is the current recapture root, which corrects in both swap phases. This is the
16
- * root's identity, not the pool's: a pool opened on a superseded recapture root keeps the executor
17
- * frozen into its rules and keeps correcting, and this returns false for it. Read that from the
18
- * pool itself with `poolCorrects(await readPoolRules(client, poolId))` and pass the answer as
19
- * `correcting` to the swap builders.
20
- */
21
- export function isCorrectingRoot(hooks) {
22
- return hooks.toLowerCase() === ADDRESSES.recaptureRoot.toLowerCase();
23
- }
24
- /** The gas limit to send with a swap on `key`: the floor when the pool corrects (`correcting`,
25
- * defaulting to `isCorrectingRoot(key.hooks)`), else `undefined` (let the wallet estimate). */
26
- export function swapGasLimit(key, correcting) {
27
- return (correcting ?? isCorrectingRoot(key.hooks)) ? CORRECTION_SWAP_GAS_FLOOR : undefined;
28
- }
29
- /** Router / block `MIN_SQRT_PRICE_LIMIT`: the full-fill limit for a `zeroForOne` swap. */
30
- export const MIN_SQRT_PRICE_LIMIT = 4295128740n;
31
- /** Router / block `MAX_SQRT_PRICE_LIMIT`: the full-fill limit for a `oneForZero` swap. */
32
- export const MAX_SQRT_PRICE_LIMIT = 1461446703485210103287273052203988822378723970341n;
33
- /** `type(int128).max`: the router's and the quoter's ceiling on every amount and bound. */
34
- export const INT128_MAX = (1n << 127n) - 1n;
35
- /** The bound an exact-input quote carries: the smallest value the quoter admits. */
36
- export const QUOTE_MIN_OUT_BOUND = 1n;
37
- /** The router's `moduleData` for a plain swap: the native block reads none. */
38
- export const EMPTY_MODULE_DATA = "0x";
39
- /** The full-fill `sqrtPriceLimitX96`: `MIN` when selling currency0, `MAX` otherwise. Any pool
40
- * with an input cut rejects every other limit on an exact-input buy. */
41
- export function fullFillLimit(zeroForOne) {
42
- return zeroForOne ? MIN_SQRT_PRICE_LIMIT : MAX_SQRT_PRICE_LIMIT;
43
- }
44
- /** `zeroForOne = currencyIn == key.currency0`. Throws when `currencyIn` is not in the pool. */
45
- export function swapDirection(key, currencyIn) {
46
- const same = (a, b) => a.toLowerCase() === b.toLowerCase();
47
- if (same(currencyIn, key.currency0))
48
- return true;
49
- if (same(currencyIn, key.currency1))
50
- return false;
51
- throw new Error("That currency is not in this pool.");
52
- }
53
- /**
54
- * `exactInput` through the Hookr router. `value` is `amountIn` when `currencyIn` is native ETH,
55
- * else 0 (the router pulls the ERC-20 by `transferFrom`; approve it first). `gas` carries the
56
- * correction floor when the pool corrects; send it as the gas limit. `correcting` defaults to
57
- * `isCorrectingRoot(key.hooks)`; pass `poolCorrects(rules)` for a pool on a superseded root.
58
- */
59
- export function buildExactInputSwapCall(input) {
60
- const zeroForOne = swapDirection(input.key, input.currencyIn);
61
- const native = input.currencyIn.toLowerCase() === NATIVE_ADDRESS.toLowerCase();
62
- const gas = swapGasLimit(input.key, input.correcting);
63
- return Object.freeze({
64
- address: ADDRESSES.router,
65
- abi: hookrKernelRouterV3Abi,
66
- functionName: "exactInput",
67
- args: [
68
- {
69
- key: input.key,
70
- zeroForOne,
71
- amountIn: input.amountIn,
72
- amountOutMinimum: input.amountOutMinimum,
73
- sqrtPriceLimitX96: fullFillLimit(zeroForOne),
74
- recipient: input.recipient,
75
- deadline: input.deadline,
76
- },
77
- EMPTY_MODULE_DATA,
78
- ],
79
- value: native ? input.amountIn : 0n,
80
- ...(gas === undefined ? {} : { gas }),
81
- });
82
- }
83
- /**
84
- * `exactOutput` through the Hookr router. `value` is `amountInMaximum` when `currencyIn` is
85
- * native ETH — the router refunds what the swap does not use. `correcting` works as on
86
- * `buildExactInputSwapCall`.
87
- */
88
- export function buildExactOutputSwapCall(input) {
89
- const zeroForOne = swapDirection(input.key, input.currencyIn);
90
- const native = input.currencyIn.toLowerCase() === NATIVE_ADDRESS.toLowerCase();
91
- const gas = swapGasLimit(input.key, input.correcting);
92
- return Object.freeze({
93
- address: ADDRESSES.router,
94
- abi: hookrKernelRouterV3Abi,
95
- functionName: "exactOutput",
96
- args: [
97
- {
98
- key: input.key,
99
- zeroForOne,
100
- amountOut: input.amountOut,
101
- amountInMaximum: input.amountInMaximum,
102
- sqrtPriceLimitX96: fullFillLimit(zeroForOne),
103
- recipient: input.recipient,
104
- deadline: input.deadline,
105
- },
106
- EMPTY_MODULE_DATA,
107
- ],
108
- value: native ? input.amountInMaximum : 0n,
109
- ...(gas === undefined ? {} : { gas }),
110
- });
111
- }
112
- /**
113
- * Quote through `kernelQuoter.quote`. Run as a simulated call from `account` rather than an
114
- * ordinary read: the function is declared `nonpayable` (it runs the full module loop, including
115
- * the pot leg, which is recipient-specific), which is also why the app's own equivalent
116
- * (`quoteV2` in `src/lib/v2-swap.ts`) calls `simulateContract` rather than `readContract` — the
117
- * same `eth_call` either way, just the viem entry point whose types match a nonpayable function.
118
- * `amountSpecified` is negative for an exact-input quote, bound at 1 wei/unit — the least the
119
- * quoter admits; pass `amountBound` to override it (an exact-output quote wants `INT128_MAX` or
120
- * your own maximum).
121
- */
122
- export async function quoteExactInput(client, input) {
123
- const zeroForOne = swapDirection(input.key, input.currencyIn);
124
- const { result } = await client.simulateContract({
125
- address: ADDRESSES.quoter,
126
- abi: hookrKernelQuoterV1Abi,
127
- functionName: "quote",
128
- account: input.user,
129
- args: [
130
- {
131
- key: input.key,
132
- payer: input.user,
133
- recipient: input.user,
134
- zeroForOne,
135
- amountSpecified: -input.amountIn,
136
- amountBound: input.amountBound ?? QUOTE_MIN_OUT_BOUND,
137
- sqrtPriceLimitX96: fullFillLimit(zeroForOne),
138
- },
139
- EMPTY_MODULE_DATA,
140
- ],
141
- });
142
- const [amountIn, amountOut] = result;
143
- return Object.freeze({ amountIn, amountOut });
144
- }