hookr-sdk 0.2.1 → 1.0.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 (66) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATING.md +89 -0
  4. package/README.md +286 -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 +6 -15
  44. package/dist/index.js +6 -15
  45. package/dist/releases.d.ts +172 -0
  46. package/dist/releases.js +268 -0
  47. package/package.json +46 -10
  48. package/provenance.json +31 -0
  49. package/dist/abi.d.ts +0 -6404
  50. package/dist/abi.js +0 -8300
  51. package/dist/chain.d.ts +0 -87
  52. package/dist/chain.js +0 -86
  53. package/dist/claims.d.ts +0 -32
  54. package/dist/claims.js +0 -31
  55. package/dist/fees.d.ts +0 -59
  56. package/dist/fees.js +0 -142
  57. package/dist/links.d.ts +0 -48
  58. package/dist/links.js +0 -93
  59. package/dist/market.d.ts +0 -264
  60. package/dist/market.js +0 -310
  61. package/dist/reads.d.ts +0 -136
  62. package/dist/reads.js +0 -219
  63. package/dist/rules.d.ts +0 -84
  64. package/dist/rules.js +0 -296
  65. package/dist/swap.d.ts +0 -108
  66. package/dist/swap.js +0 -144
package/dist/chain.d.ts DELETED
@@ -1,87 +0,0 @@
1
- import type { Address, Hex } from "viem";
2
- /**
3
- * Robinhood Chain, and Hookr's two live root hooks on it.
4
- *
5
- * Every value here is a literal copy of `src/lib/v2-deployments.ts` (the addresses, read from
6
- * the app's own deployment evidence) and `src/lib/chain.ts` (the RPC and explorer origin) in the
7
- * Hookr app. `src/test/hookr-sdk-parity.test.ts` in that app asserts this file agrees with both,
8
- * so a redeploy or an RPC change that is not mirrored here fails that suite.
9
- *
10
- * Two roots take new markets today, and a pool names exactly one of them in `PoolKey.hooks`:
11
- * `HookrModularHookV6` (`ADDRESSES.root`, the default root: the five native rules) and
12
- * `HookrModularHookV6WthV5` (`ADDRESSES.recaptureRoot`: the same five rules plus the arbitrage
13
- * recapture lane, whose correction executor is run by WTH). `ROOTS` pairs each with the kernel
14
- * id the coordinator opens it under. Earlier recapture roots still hold trading pools; resolve
15
- * any other root from the registry rather than from a list.
16
- */
17
- /** Robinhood Chain's id. */
18
- export declare const CHAIN_ID = 4663;
19
- /**
20
- * The chain's public, keyless RPC endpoint. Use it for light reads only: under load it rate
21
- * limits, and an over-wide `eth_getLogs` can come back as an empty array instead of an error.
22
- * Anything that scans logs or must not miss data should run on a provider endpoint you own and
23
- * check for failed ranges; see the README.
24
- */
25
- export declare const RPC_URL = "https://rpc.mainnet.chain.robinhood.com";
26
- /** Origin of the chain's block explorer (Blockscout). No trailing slash. */
27
- export declare const EXPLORER_URL = "https://robinhoodchain.blockscout.com";
28
- /** The V2 contracts and ids a third-party integration calls or reads. */
29
- export declare const ADDRESSES: Readonly<{
30
- /** `HookrModularHookV6` — the default root, `hooks` in every pool opened on it. */
31
- root: Address;
32
- /** `HookrModularHookV6WthV5` — the arbitrage-recapture root, `hooks` in every pool opened on it. */
33
- recaptureRoot: Address;
34
- /** `HookrMarketCoordinatorV5` — opens markets, collects founding fees, resolves the share tier. */
35
- coordinator: Address;
36
- /** `HookrKernelRouterV3` — the swap router that pays into and out of the pot. */
37
- router: Address;
38
- /** `HookrKernelQuoterV1` — the quoter that runs the full module loop. */
39
- quoter: Address;
40
- /** `HookrNativeMechanicsBlockV2` — the one native block every V2 stack carries; also where claims are paid. */
41
- nativeBlock: Address;
42
- /** `HookrStackRegistryV2` — read-only, a pool's frozen config and module list. */
43
- registry: Address;
44
- /** `HookrModuleCatalogV1` — the on-chain catalog of admitted modules. */
45
- catalog: Address;
46
- /** `HookrReleaseCreate2FactoryV1` — the CREATE2 factory the release was deployed through. */
47
- factory: Address;
48
- }>;
49
- /** The native-mechanics module id every V2 pool's one module is keyed by in the registry. */
50
- export declare const MODULE_ID: Hex;
51
- /** The coordinator's kernel id for the default root, carried in `MarketParams.kernelId`. */
52
- export declare const KERNEL_ID: Hex;
53
- /** The coordinator's kernel id for the recapture root. */
54
- export declare const RECAPTURE_KERNEL_ID: Hex;
55
- /** The two live roots, each with the kernel id a market opens under on it. */
56
- export declare const ROOTS: Readonly<{
57
- default: Readonly<{
58
- hook: `0x${string}`;
59
- kernelId: `0x${string}`;
60
- }>;
61
- recapture: Readonly<{
62
- hook: `0x${string}`;
63
- kernelId: `0x${string}`;
64
- }>;
65
- }>;
66
- /**
67
- * The frozen `StackLimits` correction fields a market on the recapture root must carry for its
68
- * correction lane to exist at all (the app's `V2_RECAPTURE_CORRECTION`). The kernel returns before
69
- * correcting when a pool's frozen `correctionExecutor` is zero, and the fields are part of the stack
70
- * hash, so they are set at creation or never. `executor` is the adapter the current recapture root's
71
- * profile admits, bound immutably to WTH's executor; `feePolicyId` is the id that adapter answers
72
- * with; `maxVolumeBps` and `minProfitQuote` are the registry's permissive ceiling and its smallest
73
- * legal floor, frozen but not enforced (WTH sizes its own corrections). The registry admits a
74
- * correction executor only on a native-ETH-quoted pool, and the pool's creator is frozen beside them.
75
- */
76
- export declare const RECAPTURE_CORRECTION: Readonly<{
77
- executor: Address;
78
- feePolicyId: Hex;
79
- maxVolumeBps: 5000;
80
- minProfitQuote: 1n;
81
- }>;
82
- /** True when `hooks` is the recapture root, the one root whose pools carry a correction lane. */
83
- export declare function isRecaptureRoot(hooks: Address): boolean;
84
- /** Native ETH as a v4 currency: the zero address. Always `currency0` when it is one side of a pool. */
85
- export declare const NATIVE_ADDRESS: Address;
86
- export declare function explorerAddressUrl(address: Address): string;
87
- export declare function explorerTxUrl(hash: Hex): string;
package/dist/chain.js DELETED
@@ -1,86 +0,0 @@
1
- /**
2
- * Robinhood Chain, and Hookr's two live root hooks on it.
3
- *
4
- * Every value here is a literal copy of `src/lib/v2-deployments.ts` (the addresses, read from
5
- * the app's own deployment evidence) and `src/lib/chain.ts` (the RPC and explorer origin) in the
6
- * Hookr app. `src/test/hookr-sdk-parity.test.ts` in that app asserts this file agrees with both,
7
- * so a redeploy or an RPC change that is not mirrored here fails that suite.
8
- *
9
- * Two roots take new markets today, and a pool names exactly one of them in `PoolKey.hooks`:
10
- * `HookrModularHookV6` (`ADDRESSES.root`, the default root: the five native rules) and
11
- * `HookrModularHookV6WthV5` (`ADDRESSES.recaptureRoot`: the same five rules plus the arbitrage
12
- * recapture lane, whose correction executor is run by WTH). `ROOTS` pairs each with the kernel
13
- * id the coordinator opens it under. Earlier recapture roots still hold trading pools; resolve
14
- * any other root from the registry rather than from a list.
15
- */
16
- /** Robinhood Chain's id. */
17
- export const CHAIN_ID = 4663;
18
- /**
19
- * The chain's public, keyless RPC endpoint. Use it for light reads only: under load it rate
20
- * limits, and an over-wide `eth_getLogs` can come back as an empty array instead of an error.
21
- * Anything that scans logs or must not miss data should run on a provider endpoint you own and
22
- * check for failed ranges; see the README.
23
- */
24
- export const RPC_URL = "https://rpc.mainnet.chain.robinhood.com";
25
- /** Origin of the chain's block explorer (Blockscout). No trailing slash. */
26
- export const EXPLORER_URL = "https://robinhoodchain.blockscout.com";
27
- /** The V2 contracts and ids a third-party integration calls or reads. */
28
- export const ADDRESSES = Object.freeze({
29
- /** `HookrModularHookV6` — the default root, `hooks` in every pool opened on it. */
30
- root: "0xb3cA29cF721380CEe8b8e4755F3865Ebc68Fe8cC",
31
- /** `HookrModularHookV6WthV5` — the arbitrage-recapture root, `hooks` in every pool opened on it. */
32
- recaptureRoot: "0xb914f955294799de4b891bd2EA8AF628Fa1c68CC",
33
- /** `HookrMarketCoordinatorV5` — opens markets, collects founding fees, resolves the share tier. */
34
- coordinator: "0x53A192A3fCeE94Da77916B461E0cCa2Dd6402442",
35
- /** `HookrKernelRouterV3` — the swap router that pays into and out of the pot. */
36
- router: "0xf0E528c39f33F565876cbaa7e0DFaCa38Df966E9",
37
- /** `HookrKernelQuoterV1` — the quoter that runs the full module loop. */
38
- quoter: "0x5Ba8FBbB4aB20Ff6Daf0ecDEBe3784f869BcB1B5",
39
- /** `HookrNativeMechanicsBlockV2` — the one native block every V2 stack carries; also where claims are paid. */
40
- nativeBlock: "0xD700492b504ba5A72D7de28dDe11Cd7985a7F1ae",
41
- /** `HookrStackRegistryV2` — read-only, a pool's frozen config and module list. */
42
- registry: "0x5b7f1A117A83aaac15B0698Aa4eB9D53fE5f5BA3",
43
- /** `HookrModuleCatalogV1` — the on-chain catalog of admitted modules. */
44
- catalog: "0x6a3561d5B6305E2229F30EDF66702e8Bc4fA19D1",
45
- /** `HookrReleaseCreate2FactoryV1` — the CREATE2 factory the release was deployed through. */
46
- factory: "0xBc54e888C1A5B71744B035DfeC5611B4DC69ccDe",
47
- });
48
- /** The native-mechanics module id every V2 pool's one module is keyed by in the registry. */
49
- export const MODULE_ID = "0x961091565aaf4eaa4996296fda9772957e61d18d90b965111a59ded2bbb5e774";
50
- /** The coordinator's kernel id for the default root, carried in `MarketParams.kernelId`. */
51
- export const KERNEL_ID = "0x1be0c118b1c6520d97de31ee9f0c33069f0e715ffcdcb16c87a343752bb5be14";
52
- /** The coordinator's kernel id for the recapture root. */
53
- export const RECAPTURE_KERNEL_ID = "0xd8b6c165b82efc3b7498081f071ea4f2476e2fb9c61b2e614aba2a016f94555a";
54
- /** The two live roots, each with the kernel id a market opens under on it. */
55
- export const ROOTS = Object.freeze({
56
- default: Object.freeze({ hook: ADDRESSES.root, kernelId: KERNEL_ID }),
57
- recapture: Object.freeze({ hook: ADDRESSES.recaptureRoot, kernelId: RECAPTURE_KERNEL_ID }),
58
- });
59
- /**
60
- * The frozen `StackLimits` correction fields a market on the recapture root must carry for its
61
- * correction lane to exist at all (the app's `V2_RECAPTURE_CORRECTION`). The kernel returns before
62
- * correcting when a pool's frozen `correctionExecutor` is zero, and the fields are part of the stack
63
- * hash, so they are set at creation or never. `executor` is the adapter the current recapture root's
64
- * profile admits, bound immutably to WTH's executor; `feePolicyId` is the id that adapter answers
65
- * with; `maxVolumeBps` and `minProfitQuote` are the registry's permissive ceiling and its smallest
66
- * legal floor, frozen but not enforced (WTH sizes its own corrections). The registry admits a
67
- * correction executor only on a native-ETH-quoted pool, and the pool's creator is frozen beside them.
68
- */
69
- export const RECAPTURE_CORRECTION = Object.freeze({
70
- executor: "0x28AF7A3645080e926a3101461e0Ec0594D42D806",
71
- feePolicyId: "0xd2653e091cb7002585fd8b1192b58f11b9c951061dc797123e37d5e2ccb45cef",
72
- maxVolumeBps: 5_000,
73
- minProfitQuote: 1n,
74
- });
75
- /** True when `hooks` is the recapture root, the one root whose pools carry a correction lane. */
76
- export function isRecaptureRoot(hooks) {
77
- return hooks.toLowerCase() === ADDRESSES.recaptureRoot.toLowerCase();
78
- }
79
- /** Native ETH as a v4 currency: the zero address. Always `currency0` when it is one side of a pool. */
80
- export const NATIVE_ADDRESS = "0x0000000000000000000000000000000000000000";
81
- export function explorerAddressUrl(address) {
82
- return `${EXPLORER_URL}/address/${address}`;
83
- }
84
- export function explorerTxUrl(hash) {
85
- return `${EXPLORER_URL}/tx/${hash}`;
86
- }
package/dist/claims.d.ts DELETED
@@ -1,32 +0,0 @@
1
- /**
2
- * Claim what the native block owes: pot winnings and royalties, pooled per quote currency
3
- * across every pool. Copied from the app's `src/lib/agent-skill.ts` ("Claim"):
4
- *
5
- * nativeMechanicsBlock.claimable(quote, account) view returns (uint256)
6
- * nativeMechanicsBlock.claim(quote) // pays msg.sender
7
- * nativeMechanicsBlock.claimTo(quote, to) // pays to
8
- *
9
- * `claim`/`claimTo` pay pot winnings and royalties; read `claimable` first, since a zero balance
10
- * reverts. `quote` is the zero address for a claim in native ETH.
11
- */
12
- import type { Address } from "viem";
13
- import { hookrNativeMechanicsBlockV2Abi } from "./abi.js";
14
- export type ReadCall<F extends string> = Readonly<{
15
- address: Address;
16
- abi: typeof hookrNativeMechanicsBlockV2Abi;
17
- functionName: F;
18
- args: readonly unknown[];
19
- }>;
20
- export type WriteCall<F extends string> = Readonly<{
21
- address: Address;
22
- abi: typeof hookrNativeMechanicsBlockV2Abi;
23
- functionName: F;
24
- args: readonly unknown[];
25
- value: bigint;
26
- }>;
27
- /** `nativeMechanicsBlock.claimable(quote, account)`. Read with viem's `readContract`. */
28
- export declare function claimableCall(quote: Address, account: Address): ReadCall<"claimable">;
29
- /** `nativeMechanicsBlock.claim(quote)`: pays `msg.sender`. Reverts on a zero balance. */
30
- export declare function claimCall(quote: Address): WriteCall<"claim">;
31
- /** `nativeMechanicsBlock.claimTo(quote, to)`: pays `to`. Reverts on a zero balance. */
32
- export declare function claimToCall(quote: Address, to: Address): WriteCall<"claimTo">;
package/dist/claims.js DELETED
@@ -1,31 +0,0 @@
1
- import { hookrNativeMechanicsBlockV2Abi } from "./abi.js";
2
- import { ADDRESSES } from "./chain.js";
3
- /** `nativeMechanicsBlock.claimable(quote, account)`. Read with viem's `readContract`. */
4
- export function claimableCall(quote, account) {
5
- return Object.freeze({
6
- address: ADDRESSES.nativeBlock,
7
- abi: hookrNativeMechanicsBlockV2Abi,
8
- functionName: "claimable",
9
- args: [quote, account],
10
- });
11
- }
12
- /** `nativeMechanicsBlock.claim(quote)`: pays `msg.sender`. Reverts on a zero balance. */
13
- export function claimCall(quote) {
14
- return Object.freeze({
15
- address: ADDRESSES.nativeBlock,
16
- abi: hookrNativeMechanicsBlockV2Abi,
17
- functionName: "claim",
18
- args: [quote],
19
- value: 0n,
20
- });
21
- }
22
- /** `nativeMechanicsBlock.claimTo(quote, to)`: pays `to`. Reverts on a zero balance. */
23
- export function claimToCall(quote, to) {
24
- return Object.freeze({
25
- address: ADDRESSES.nativeBlock,
26
- abi: hookrNativeMechanicsBlockV2Abi,
27
- functionName: "claimTo",
28
- args: [quote, to],
29
- value: 0n,
30
- });
31
- }
package/dist/fees.d.ts DELETED
@@ -1,59 +0,0 @@
1
- /**
2
- * The V2 fee model: base fee bounds, and the per-stream recipient/protocol split.
3
- *
4
- * Bounds are copied from the app's `src/lib/v2-fee-tiers.ts` and `src/lib/v2-combinations.ts`.
5
- * `feeModel` walks a stack exactly the way `src/lib/v2-fee-streams.ts`'s `buildV2FeeStreams`
6
- * does — same streams, same order, same split arithmetic — restated in wei terms at a supplied
7
- * sample amount (default one whole unit, 1e18) rather than the app's formatted, labelled rows.
8
- * `src/test/hookr-sdk-parity.test.ts` calls both on the same three configurations and the same
9
- * sample amount and asserts the wei splits agree.
10
- */
11
- import { type RuleBlock } from "./rules.js";
12
- /** `HookrNativeMechanicsBlockV2.MAX_TOTAL_FEE_PIPS`: 50% of a swap, base + surge + snipe. */
13
- export declare const MAX_TOTAL_FEE_PIPS = 500000;
14
- /** Smallest base fee accepted. One pip is a millionth; 100 pips is 0.01%. */
15
- export declare const MIN_BASE_FEE_PIPS = 100;
16
- /** The base fee's admissible range, in pips. */
17
- export declare const baseFeeBounds: Readonly<{
18
- min: number;
19
- max: number;
20
- }>;
21
- /** `HookrMarketCoordinatorV5.defaultProtocolShareBps`: the protocol's default share of each add-on. */
22
- export declare const DEFAULT_PROTOCOL_SHARE_BPS = 2000;
23
- /** `HookrNativeMechanicsBlockV2.MAX_PROTOCOL_SHARE_BPS`: the hard ceiling on that share. */
24
- export declare const MAX_PROTOCOL_SHARE_BPS = 5000;
25
- export type FeeStreamKind = "baseFee" | "surge" | "snipeTax" | "lpReward" | "pot" | "burn";
26
- export type FeeStreamSplit = Readonly<{
27
- stream: FeeStreamKind;
28
- /** The rule that controls this stream; null for the base fee, which is not a placeable rule. */
29
- rule: "snipe" | "fee" | "burn" | "lp" | "pot" | null;
30
- /** This stream's total, in wei of the sample amount. For `burn`, the protocol's quote-side
31
- * slice only: the burned share itself is a rate on the token OUTPUT (`burnNetBps`), which is
32
- * not known until the pool prices the swap. */
33
- totalWei: bigint;
34
- recipientWei: bigint;
35
- protocolWei: bigint;
36
- /** Set only on the `burn` row: the share of token output burned, net of the protocol's slice. */
37
- burnNetBps: number | null;
38
- }>;
39
- export type FeeModelResult = Readonly<{
40
- amountWei: bigint;
41
- protocolShareBps: number;
42
- streams: readonly FeeStreamSplit[];
43
- /** Every stream's protocol slice, summed. */
44
- totalProtocolWei: bigint;
45
- }>;
46
- /**
47
- * Walk a stack of rule blocks through the fee model for one sample amount, exactly as
48
- * `buildV2FeeStreams` does: the base fee is always shown; surge, the snipe tax, the LP-reward
49
- * and pot cuts, and the burn cut each appear only while their rule is on the stack with a
50
- * nonzero rate. The snipe-tax row is shown as if the guard were open, because that is the only
51
- * state in which it is ever nonzero — the app's row says so too.
52
- *
53
- * `baseFeePips` is the stack's Base LP fee (see `compileRuleBlocks`); `amountWei` defaults to
54
- * one whole unit (1e18) of the pool's quote currency.
55
- */
56
- export declare function feeModel(baseFeePips: number, blocks: readonly RuleBlock[], opts?: Readonly<{
57
- amountWei?: bigint;
58
- protocolShareBps?: number;
59
- }>): FeeModelResult;
package/dist/fees.js DELETED
@@ -1,142 +0,0 @@
1
- /**
2
- * The V2 fee model: base fee bounds, and the per-stream recipient/protocol split.
3
- *
4
- * Bounds are copied from the app's `src/lib/v2-fee-tiers.ts` and `src/lib/v2-combinations.ts`.
5
- * `feeModel` walks a stack exactly the way `src/lib/v2-fee-streams.ts`'s `buildV2FeeStreams`
6
- * does — same streams, same order, same split arithmetic — restated in wei terms at a supplied
7
- * sample amount (default one whole unit, 1e18) rather than the app's formatted, labelled rows.
8
- * `src/test/hookr-sdk-parity.test.ts` calls both on the same three configurations and the same
9
- * sample amount and asserts the wei splits agree.
10
- */
11
- import { compileRuleBlocks } from "./rules.js";
12
- /** `HookrNativeMechanicsBlockV2.MAX_TOTAL_FEE_PIPS`: 50% of a swap, base + surge + snipe. */
13
- export const MAX_TOTAL_FEE_PIPS = 500_000;
14
- /** Smallest base fee accepted. One pip is a millionth; 100 pips is 0.01%. */
15
- export const MIN_BASE_FEE_PIPS = 100;
16
- /** The base fee's admissible range, in pips. */
17
- export const baseFeeBounds = Object.freeze({
18
- min: MIN_BASE_FEE_PIPS,
19
- max: MAX_TOTAL_FEE_PIPS,
20
- });
21
- /** `HookrMarketCoordinatorV5.defaultProtocolShareBps`: the protocol's default share of each add-on. */
22
- export const DEFAULT_PROTOCOL_SHARE_BPS = 2_000;
23
- /** `HookrNativeMechanicsBlockV2.MAX_PROTOCOL_SHARE_BPS`: the hard ceiling on that share. */
24
- export const MAX_PROTOCOL_SHARE_BPS = 5_000;
25
- const BPS = 10000n;
26
- const PIPS = 1000000n;
27
- function pipsOf(pips, baseWei) {
28
- return (baseWei * BigInt(Math.max(0, Math.round(pips)))) / PIPS;
29
- }
30
- function bpsOf(bps, baseWei) {
31
- return (baseWei * BigInt(Math.max(0, Math.round(bps)))) / BPS;
32
- }
33
- /** A stream's total split into the recipient's share and the protocol's, per the fee model: the
34
- * protocol's share is always a floor of the total, never above the frozen rate. */
35
- function splitByShare(totalWei, protocolShareBps) {
36
- const bounded = Math.max(0, Math.min(10_000, Math.round(protocolShareBps)));
37
- const protocol = (totalWei * BigInt(bounded)) / BPS;
38
- return { recipient: totalWei - protocol, protocol };
39
- }
40
- /**
41
- * Walk a stack of rule blocks through the fee model for one sample amount, exactly as
42
- * `buildV2FeeStreams` does: the base fee is always shown; surge, the snipe tax, the LP-reward
43
- * and pot cuts, and the burn cut each appear only while their rule is on the stack with a
44
- * nonzero rate. The snipe-tax row is shown as if the guard were open, because that is the only
45
- * state in which it is ever nonzero — the app's row says so too.
46
- *
47
- * `baseFeePips` is the stack's Base LP fee (see `compileRuleBlocks`); `amountWei` defaults to
48
- * one whole unit (1e18) of the pool's quote currency.
49
- */
50
- export function feeModel(baseFeePips, blocks, opts = {}) {
51
- const amountWei = opts.amountWei ?? 10n ** 18n;
52
- const protocolShareBps = opts.protocolShareBps ?? DEFAULT_PROTOCOL_SHARE_BPS;
53
- const compiled = compileRuleBlocks(blocks, baseFeePips);
54
- const { maxFeePips, snipeTaxPips, lpBps, potBps, burnBps } = compiled;
55
- const base = Math.max(0, compiled.baseFeePips);
56
- // The gross LP-reward-plus-pot cut, exactly as the app's `buildBuyWaterfall` computes it with
57
- // royaltyBps pinned to 0 (the same pinning `buildV2FeeStreams` uses): one bigint division of
58
- // the sample amount by the combined cut, then split between the two rules by their own share
59
- // of that combined cut — never re-derived from the amount separately, so the same rounding
60
- // dust lands in the same place (the pot) that the app's walk does.
61
- const totalCutBps = lpBps + potBps;
62
- const grossCutWei = totalCutBps > 0 ? (amountWei * BigInt(totalCutBps)) / BPS : 0n;
63
- const lpRewardTotalWei = totalCutBps > 0 ? (grossCutWei * BigInt(lpBps)) / BigInt(totalCutBps) : 0n;
64
- const potTotalWei = grossCutWei - lpRewardTotalWei;
65
- const baseWei = pipsOf(base, amountWei);
66
- const surgeTotalWei = pipsOf(Math.max(0, maxFeePips - base), amountWei);
67
- const snipeTotalWei = pipsOf(snipeTaxPips, amountWei);
68
- const surgeSplit = splitByShare(surgeTotalWei, protocolShareBps);
69
- const snipeSplit = splitByShare(snipeTotalWei, protocolShareBps);
70
- const lpRewardSplit = splitByShare(lpRewardTotalWei, protocolShareBps);
71
- const potSplit = splitByShare(potTotalWei, protocolShareBps);
72
- const burnProtocolBps = Math.floor((burnBps * protocolShareBps) / 10_000);
73
- const burnNetBps = burnBps - burnProtocolBps;
74
- const burnProtocolWei = bpsOf(burnProtocolBps, amountWei);
75
- const streams = [
76
- {
77
- stream: "baseFee",
78
- rule: null,
79
- totalWei: baseWei,
80
- recipientWei: baseWei,
81
- protocolWei: 0n,
82
- burnNetBps: null,
83
- },
84
- ];
85
- if (maxFeePips > base) {
86
- streams.push({
87
- stream: "surge",
88
- rule: "fee",
89
- totalWei: surgeTotalWei,
90
- recipientWei: surgeSplit.recipient,
91
- protocolWei: surgeSplit.protocol,
92
- burnNetBps: null,
93
- });
94
- }
95
- if (snipeTaxPips > 0) {
96
- streams.push({
97
- stream: "snipeTax",
98
- rule: "snipe",
99
- totalWei: snipeTotalWei,
100
- recipientWei: snipeSplit.recipient,
101
- protocolWei: snipeSplit.protocol,
102
- burnNetBps: null,
103
- });
104
- }
105
- if (lpBps > 0) {
106
- streams.push({
107
- stream: "lpReward",
108
- rule: "lp",
109
- totalWei: lpRewardTotalWei,
110
- recipientWei: lpRewardSplit.recipient,
111
- protocolWei: lpRewardSplit.protocol,
112
- burnNetBps: null,
113
- });
114
- }
115
- if (potBps > 0) {
116
- streams.push({
117
- stream: "pot",
118
- rule: "pot",
119
- totalWei: potTotalWei,
120
- recipientWei: potSplit.recipient,
121
- protocolWei: potSplit.protocol,
122
- burnNetBps: null,
123
- });
124
- }
125
- if (burnBps > 0) {
126
- streams.push({
127
- stream: "burn",
128
- rule: "burn",
129
- totalWei: burnProtocolWei,
130
- recipientWei: 0n,
131
- protocolWei: burnProtocolWei,
132
- burnNetBps,
133
- });
134
- }
135
- const totalProtocolWei = surgeSplit.protocol + snipeSplit.protocol + lpRewardSplit.protocol + potSplit.protocol + burnProtocolWei;
136
- return Object.freeze({
137
- amountWei,
138
- protocolShareBps,
139
- streams: Object.freeze(streams),
140
- totalProtocolWei,
141
- });
142
- }
package/dist/links.d.ts DELETED
@@ -1,48 +0,0 @@
1
- /**
2
- * Deep links into Hookr's builder, in the app's "remix" wire format.
3
- *
4
- * Copied from `src/lib/remix.ts` in the app (`encodeStack`, `decodeStack`,
5
- * `decodeStackBaseFeePips`): one segment per rule, values in `PARAMS[type]` key order, plus an
6
- * optional trailing `base:<percent>` segment for the stack's Base LP fee — written only when it
7
- * differs from the launchpad's immutable default, so a link encoded before that segment existed
8
- * keeps decoding exactly as it always did. `src/test/hookr-sdk-parity.test.ts` asserts this
9
- * module's `encodeStack`/`decodeStack` round-trip through the app's own functions.
10
- *
11
- * The app hydrates a `?stack=`/`?from=` link on its `/builder` route only (`decodeRemix`, read
12
- * there); there is no separate "ready to launch" page that accepts one yet. `launchLink` and
13
- * `builderLink` both resolve to that one route today for that reason — kept as two names so a
14
- * caller can use whichever fits its own UI, and so this package does not need to change if the
15
- * app ever does split them.
16
- */
17
- import type { Address } from "viem";
18
- import { type RuleBlock } from "./rules.js";
19
- /** `hooks-model.ts`'s `DEFAULT_BASE_FEE_PIPS`: 0.3%, the launchpad's immutable default. */
20
- export declare const DEFAULT_BASE_FEE_PIPS = 3000;
21
- export declare const DEFAULT_ORIGIN = "https://hookr.fun";
22
- /** `stack:` value for a set of rule blocks and an optional stack-level Base LP fee (pips). */
23
- export declare function encodeStack(blocks: readonly RuleBlock[], baseFeePips?: number): string;
24
- /**
25
- * Parse a `stack:` value. Null when nothing usable is present. Every parameter is clamped to
26
- * its rule's bounds — a remix is a starting point, not a replica.
27
- */
28
- export declare function decodeStack(value: string | null | undefined): RuleBlock[] | null;
29
- /** The stack-level Base LP fee out of a `stack:` value, in pips. `DEFAULT_BASE_FEE_PIPS` when absent. */
30
- export declare function decodeStackBaseFeePips(value: string | null | undefined): number;
31
- export type LinkConfig = Readonly<{
32
- blocks: readonly RuleBlock[];
33
- /** The stack's Base LP fee, in pips. Omit to leave it at the launchpad default. */
34
- baseFeePips?: number;
35
- }>;
36
- export type LinkOptions = Readonly<{
37
- /** Defaults to `https://hookr.fun`. */
38
- origin?: string;
39
- /** The token this configuration was copied from, for the "from" query param. */
40
- from?: Address;
41
- }>;
42
- /** A link into Hookr's builder with this configuration pre-loaded, for exploring or tweaking it. */
43
- export declare function builderLink(config: LinkConfig, opts?: LinkOptions): string;
44
- /**
45
- * A link into Hookr to open a market with this configuration. Resolves to the same `/builder`
46
- * route as `builderLink` today — see the module doc.
47
- */
48
- export declare function launchLink(config: LinkConfig, opts?: LinkOptions): string;
package/dist/links.js DELETED
@@ -1,93 +0,0 @@
1
- import { MAX_TOTAL_FEE_PIPS, MIN_BASE_FEE_PIPS } from "./fees.js";
2
- import { PARAMS, RULE_ORDER } from "./rules.js";
3
- /** `hooks-model.ts`'s `DEFAULT_BASE_FEE_PIPS`: 0.3%, the launchpad's immutable default. */
4
- export const DEFAULT_BASE_FEE_PIPS = 3_000;
5
- export const DEFAULT_ORIGIN = "https://hookr.fun";
6
- const BASE_FEE_SEGMENT = "base";
7
- function trimNumber(value) {
8
- if (!Number.isFinite(value))
9
- return "0";
10
- return String(Number(value.toPrecision(12)));
11
- }
12
- function isRuleKey(value) {
13
- return RULE_ORDER.includes(value);
14
- }
15
- /** `stack:` value for a set of rule blocks and an optional stack-level Base LP fee (pips). */
16
- export function encodeStack(blocks, baseFeePips) {
17
- const blockSegments = blocks.map((block) => {
18
- const values = PARAMS[block.type].map((spec) => trimNumber(block.params[spec.key] ?? spec.def));
19
- return `${block.type}:${values.join(",")}`;
20
- });
21
- const segments = baseFeePips !== undefined && Math.round(baseFeePips) !== DEFAULT_BASE_FEE_PIPS
22
- ? [...blockSegments, `${BASE_FEE_SEGMENT}:${trimNumber(baseFeePips / 10_000)}`]
23
- : blockSegments;
24
- return segments.join("|");
25
- }
26
- /**
27
- * Parse a `stack:` value. Null when nothing usable is present. Every parameter is clamped to
28
- * its rule's bounds — a remix is a starting point, not a replica.
29
- */
30
- export function decodeStack(value) {
31
- if (!value)
32
- return null;
33
- const blocks = [];
34
- const seen = new Set();
35
- let sawBaseFeeSegment = false;
36
- for (const segment of value.split("|")) {
37
- const [type, list] = segment.split(":");
38
- if (type === BASE_FEE_SEGMENT) {
39
- sawBaseFeeSegment = true;
40
- continue;
41
- }
42
- if (!type || !isRuleKey(type) || seen.has(type))
43
- continue;
44
- const specs = PARAMS[type];
45
- const parts = (list ?? "").split(",");
46
- const params = {};
47
- specs.forEach((spec, i) => {
48
- const parsed = Number(parts[i]);
49
- const v = Number.isFinite(parsed) ? parsed : spec.def;
50
- params[spec.key] = Math.min(spec.max, Math.max(spec.min, v));
51
- });
52
- seen.add(type);
53
- blocks.push({ type, params: Object.freeze(params) });
54
- }
55
- return blocks.length > 0 || sawBaseFeeSegment ? blocks : null;
56
- }
57
- /** The stack-level Base LP fee out of a `stack:` value, in pips. `DEFAULT_BASE_FEE_PIPS` when absent. */
58
- export function decodeStackBaseFeePips(value) {
59
- if (!value)
60
- return DEFAULT_BASE_FEE_PIPS;
61
- for (const segment of value.split("|")) {
62
- const [type, list] = segment.split(":");
63
- if (type !== BASE_FEE_SEGMENT)
64
- continue;
65
- const parsed = Number(list);
66
- if (!Number.isFinite(parsed))
67
- continue;
68
- const pips = Math.round(parsed * 10_000);
69
- return Math.min(MAX_TOTAL_FEE_PIPS, Math.max(MIN_BASE_FEE_PIPS, pips));
70
- }
71
- return DEFAULT_BASE_FEE_PIPS;
72
- }
73
- function stackHref(path, config, opts = {}) {
74
- const stack = encodeStack(config.blocks, config.baseFeePips);
75
- const query = new URLSearchParams();
76
- if (stack)
77
- query.set("stack", stack);
78
- if (opts.from)
79
- query.set("from", opts.from);
80
- const qs = query.toString();
81
- return `${opts.origin ?? DEFAULT_ORIGIN}${path}${qs ? `?${qs}` : ""}`;
82
- }
83
- /** A link into Hookr's builder with this configuration pre-loaded, for exploring or tweaking it. */
84
- export function builderLink(config, opts) {
85
- return stackHref("/builder", config, opts);
86
- }
87
- /**
88
- * A link into Hookr to open a market with this configuration. Resolves to the same `/builder`
89
- * route as `builderLink` today — see the module doc.
90
- */
91
- export function launchLink(config, opts) {
92
- return stackHref("/builder", config, opts);
93
- }