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.
- package/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/MIGRATING.md +89 -0
- package/README.md +308 -105
- package/dist/core/abi.d.ts +10930 -0
- package/dist/core/abi.js +455 -0
- package/dist/core/advisories.d.ts +184 -0
- package/dist/core/advisories.js +291 -0
- package/dist/core/client.d.ts +55 -0
- package/dist/core/client.js +107 -0
- package/dist/core/generated.d.ts +14295 -0
- package/dist/core/generated.js +1058 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +17 -0
- package/dist/core/launch.d.ts +200 -0
- package/dist/core/launch.js +427 -0
- package/dist/core/pair.d.ts +64 -0
- package/dist/core/pair.js +136 -0
- package/dist/core/params.d.ts +415 -0
- package/dist/core/params.js +1235 -0
- package/dist/core/policies.d.ts +136 -0
- package/dist/core/policies.js +125 -0
- package/dist/core/proofs.d.ts +25 -0
- package/dist/core/proofs.js +118 -0
- package/dist/core/quotes.d.ts +185 -0
- package/dist/core/quotes.js +550 -0
- package/dist/core/referrals.d.ts +32 -0
- package/dist/core/referrals.js +36 -0
- package/dist/core/registry.d.ts +357 -0
- package/dist/core/registry.js +378 -0
- package/dist/core/rules.d.ts +675 -0
- package/dist/core/rules.js +1049 -0
- package/dist/core/schema.d.ts +358 -0
- package/dist/core/schema.js +105 -0
- package/dist/core/stocks.generated.d.ts +11 -0
- package/dist/core/stocks.generated.js +1228 -0
- package/dist/core/vault.d.ts +59 -0
- package/dist/core/vault.js +112 -0
- package/dist/core/wallet.d.ts +142 -0
- package/dist/core/wallet.js +304 -0
- package/dist/deployments.d.ts +8 -0
- package/dist/deployments.js +26 -0
- package/dist/index.d.ts +42 -15
- package/dist/index.js +40 -15
- package/dist/releases.d.ts +219 -0
- package/dist/releases.js +365 -0
- package/dist/wallet-cap.d.ts +159 -0
- package/dist/wallet-cap.js +234 -0
- package/package.json +46 -10
- package/provenance.json +31 -0
- package/dist/abi.d.ts +0 -6404
- package/dist/abi.js +0 -8300
- package/dist/chain.d.ts +0 -87
- package/dist/chain.js +0 -86
- package/dist/claims.d.ts +0 -32
- package/dist/claims.js +0 -31
- package/dist/fees.d.ts +0 -59
- package/dist/fees.js +0 -142
- package/dist/links.d.ts +0 -48
- package/dist/links.js +0 -93
- package/dist/market.d.ts +0 -264
- package/dist/market.js +0 -310
- package/dist/reads.d.ts +0 -136
- package/dist/reads.js +0 -219
- package/dist/rules.d.ts +0 -84
- package/dist/rules.js +0 -296
- package/dist/swap.d.ts +0 -108
- package/dist/swap.js +0 -144
package/dist/market.d.ts
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pool key/id, the native block's config encoding, stack limits, and the two coordinator calls
|
|
3
|
-
* that open a market.
|
|
4
|
-
*
|
|
5
|
-
* `poolKeyFor`, `poolIdFor`, the config encoder and `stackLimitsFor` are pure, faithful copies
|
|
6
|
-
* of `src/lib/v2-encode.ts` in the Hookr app (`v2PoolKey`, `v2PoolId`, `encodeV2Config`,
|
|
7
|
-
* `v2StackLimits`) — nothing here reads a chain, so `src/test/hookr-sdk-parity.test.ts` proves
|
|
8
|
-
* them by calling both on the same fixed inputs and asserting byte-for-byte agreement.
|
|
9
|
-
*
|
|
10
|
-
* `buildNewTokenMarketCall` and `buildExistingTokenMarketCall` assemble the exact
|
|
11
|
-
* `openNewTokenMarket` / `openExistingTokenMarket` calldata `src/lib/v2-launch-execute.ts`
|
|
12
|
-
* produces from a reviewed plan (`v2ExecuteTransactions`), field for field with
|
|
13
|
-
* `src/lib/v2-launch-plan.ts`'s `buildV2LaunchPlan`. Parity reached, with one deliberate scope
|
|
14
|
-
* cut, stated here rather than left implicit:
|
|
15
|
-
*
|
|
16
|
-
* - `caps` (the native block's `validateStack` answer — its surcharge and take-bps ceilings)
|
|
17
|
-
* and `protocolShareBps` / `protocolRecipient` (the coordinator's and block's own reads for
|
|
18
|
-
* this creator) are NOT re-derived here: they are live chain reads in the app
|
|
19
|
-
* (`buildV2LaunchPlan` calls `validateStack`, `protocolShareBps`, and reads the block's
|
|
20
|
-
* `protocolRecipient()`), and this package does not carry a client of its own. Pass them in
|
|
21
|
-
* — read with viem's `readContract` against `abi.ts`'s ABIs — exactly as the app's plan
|
|
22
|
-
* builder does before it ever encodes a call.
|
|
23
|
-
* - The opening `sqrtPriceX96` (and, on the new-token lane, the founding band) is a caller
|
|
24
|
-
* input too. The app derives it from a chosen price via tick math
|
|
25
|
-
* (`v2SqrtPriceForOpeningPrice`, `v2FoundingBand`) that depends on Uniswap's `TickMath`
|
|
26
|
-
* library; reimplementing and re-verifying that library was out of this pass's time box, so
|
|
27
|
-
* it is not duplicated here. Compute it with the app's own `v2-encode.ts` (or an equivalent),
|
|
28
|
-
* or read it back from an already-opened pool with `reads.ts`.
|
|
29
|
-
* - The dev buy (`initialBuy`) is accepted as an already-priced input, not computed: sizing one
|
|
30
|
-
* against the founding band (`src/lib/v2-dev-buy.ts`) is a further, separate pricing
|
|
31
|
-
* concern this pass did not reach. Omit it for a plain launch with no creator buy.
|
|
32
|
-
*
|
|
33
|
-
* Everything downstream of those three reads — the pool id, the config bytes, the stack limits,
|
|
34
|
-
* and the final struct-for-struct call — is exact, proven, and the parity test's fixed-input
|
|
35
|
-
* cases exercise the full assembly with a representative `caps`/`sqrtPriceX96` pair.
|
|
36
|
-
*/
|
|
37
|
-
import { type Address, type Hex } from "viem";
|
|
38
|
-
import { hookrMarketCoordinatorV5Abi } from "./abi.js";
|
|
39
|
-
import { type RuleBlock } from "./rules.js";
|
|
40
|
-
/** `HookrMarketCoordinatorV5.DYNAMIC_FEE_FLAG`: the Uniswap v4 dynamic-fee sentinel in every V2 PoolKey. */
|
|
41
|
-
export declare const DYNAMIC_FEE = 8388608;
|
|
42
|
-
export type PoolKey = Readonly<{
|
|
43
|
-
currency0: Address;
|
|
44
|
-
currency1: Address;
|
|
45
|
-
fee: number;
|
|
46
|
-
tickSpacing: number;
|
|
47
|
-
hooks: Address;
|
|
48
|
-
}>;
|
|
49
|
-
/** `subject < quote` by numeric value; ETH (the zero address) is therefore never `currency1`. */
|
|
50
|
-
export declare function subjectIsCurrency0(subject: Address, quote: Address): boolean;
|
|
51
|
-
/**
|
|
52
|
-
* `HookrMarketCoordinatorV5._poolKey`: currencies sorted numerically, `fee = DYNAMIC_FEE`,
|
|
53
|
-
* `hooks` the root hook: `ADDRESSES.root` (the default root) unless a pool on another root is
|
|
54
|
-
* being addressed, in which case pass that root's address (a market row's `kernel`).
|
|
55
|
-
*/
|
|
56
|
-
export declare function poolKeyFor(input: {
|
|
57
|
-
subject: Address;
|
|
58
|
-
quote: Address;
|
|
59
|
-
tickSpacing: number;
|
|
60
|
-
hooks?: Address;
|
|
61
|
-
}): PoolKey;
|
|
62
|
-
/** `PoolId = keccak256(abi.encode(key))`. */
|
|
63
|
-
export declare function poolIdFor(key: PoolKey): Hex;
|
|
64
|
-
export type NativeConfig = Readonly<{
|
|
65
|
-
poolId: Hex;
|
|
66
|
-
kernel: Address;
|
|
67
|
-
subject: Address;
|
|
68
|
-
quote: Address;
|
|
69
|
-
lockedLiquidityProvider: Address;
|
|
70
|
-
guardEndBlock: bigint;
|
|
71
|
-
baseFeePips: number;
|
|
72
|
-
maxFeePips: number;
|
|
73
|
-
snipeTaxPips: number;
|
|
74
|
-
surgeSens: number;
|
|
75
|
-
burnBps: number;
|
|
76
|
-
lpBps: number;
|
|
77
|
-
potBps: number;
|
|
78
|
-
royaltyBps: number;
|
|
79
|
-
potEveryNBuys: number;
|
|
80
|
-
maxBuyQuoteAmount: bigint;
|
|
81
|
-
potMinBuyWei: bigint;
|
|
82
|
-
royaltyTo: Address;
|
|
83
|
-
protocolRecipient: Address;
|
|
84
|
-
protocolShareBps: number;
|
|
85
|
-
}>;
|
|
86
|
-
/** `abi.encode(Config)`: one static tuple, exactly 640 bytes. */
|
|
87
|
-
export declare function encodeNativeBlockConfig(config: NativeConfig): Hex;
|
|
88
|
-
/** `HookrNativeMechanicsBlockV2.MAX_GUARD_BLOCKS`. */
|
|
89
|
-
export declare const MAX_GUARD_BLOCKS = 100000;
|
|
90
|
-
/** `guardEndBlock` for a guard of `guardBlocks` blocks from `contractBlock`; `0n` (off) at or below zero. */
|
|
91
|
-
export declare function guardEndBlockFor(contractBlock: bigint, guardBlocks: number): bigint;
|
|
92
|
-
/** The native block's registered callback gas; `limits.maxTotalModuleGas` in every fixture. */
|
|
93
|
-
export declare const MODULE_GAS = 2000000;
|
|
94
|
-
export declare const ZERO_BYTES32: Hex;
|
|
95
|
-
/** What `HookrNativeMechanicsBlockV2.validateStack` returns for a config: the surcharge and
|
|
96
|
-
* take-bps ceilings this creator's stack is admitted at. A live chain read; see the module doc. */
|
|
97
|
-
/**
|
|
98
|
-
* The root a market opens on: its hook address (`PoolKey.hooks`) and the kernel id the coordinator
|
|
99
|
-
* opens it under. `ROOTS.default` and `ROOTS.recapture` are the two current roots; the registry
|
|
100
|
-
* accepts any active kernel, so this is a product choice, not a chain constraint.
|
|
101
|
-
*/
|
|
102
|
-
export type RootChoice = Readonly<{
|
|
103
|
-
hook: Address;
|
|
104
|
-
kernelId: Hex;
|
|
105
|
-
}>;
|
|
106
|
-
export type StackCaps = Readonly<{
|
|
107
|
-
configHash: Hex;
|
|
108
|
-
maxLpFeeSurchargePips: number;
|
|
109
|
-
maxSpecifiedQuoteTakeBps: number;
|
|
110
|
-
maxUnspecifiedQuoteTakeBps: number;
|
|
111
|
-
maxSubjectTakeBps: number;
|
|
112
|
-
}>;
|
|
113
|
-
export type StackLimits = Readonly<{
|
|
114
|
-
baseLpFeePips: number;
|
|
115
|
-
maxLpFeePips: number;
|
|
116
|
-
maxSpecifiedQuoteTakeBps: number;
|
|
117
|
-
maxUnspecifiedQuoteTakeBps: number;
|
|
118
|
-
maxSubjectTakeBps: number;
|
|
119
|
-
maxTotalModuleGas: number;
|
|
120
|
-
trustedRouter: Address;
|
|
121
|
-
trustedQuoter: Address;
|
|
122
|
-
correctionExecutor: Address;
|
|
123
|
-
correctionCreator: Address;
|
|
124
|
-
correctionMaxVolumeBps: number;
|
|
125
|
-
correctionMinProfitQuote: bigint;
|
|
126
|
-
correctionFeePolicyId: Hex;
|
|
127
|
-
}>;
|
|
128
|
-
/** The correction lane frozen into a recapture-root pool's limits: `RECAPTURE_CORRECTION` plus the creator. */
|
|
129
|
-
export type CorrectionWiring = Readonly<{
|
|
130
|
-
executor: Address;
|
|
131
|
-
creator: Address;
|
|
132
|
-
maxVolumeBps: number;
|
|
133
|
-
minProfitQuote: bigint;
|
|
134
|
-
feePolicyId: Hex;
|
|
135
|
-
}>;
|
|
136
|
-
/**
|
|
137
|
-
* The correction wiring a market on `root` must freeze, or null on a root that does not correct.
|
|
138
|
-
* The registry admits a correction executor only on a native-ETH-quoted pool and requires a
|
|
139
|
-
* non-zero creator, so both are checked here rather than failing on chain.
|
|
140
|
-
*/
|
|
141
|
-
export declare function correctionFor(root: RootChoice, quote: Address, creator: Address | undefined): CorrectionWiring | null;
|
|
142
|
-
/**
|
|
143
|
-
* The tightest `StackLimits` the registry admits: equal to the block's own caps, exactly as the
|
|
144
|
-
* app's `v2StackLimits` sets them, with the correction lane frozen in when `correction` is given. `trustedRouter`/`trustedQuoter` are always `ADDRESSES.router`
|
|
145
|
-
* / `ADDRESSES.quoter` today — the app's own equivalent takes them as a parameter only so its
|
|
146
|
-
* tests can fake them; a real launch always wires the same two addresses this package does.
|
|
147
|
-
*/
|
|
148
|
-
export declare function stackLimitsFor(config: NativeConfig, caps: StackCaps, correction?: CorrectionWiring | null): StackLimits;
|
|
149
|
-
export type ModuleSelection = Readonly<{
|
|
150
|
-
moduleId: Hex;
|
|
151
|
-
config: Hex;
|
|
152
|
-
}>;
|
|
153
|
-
export type MarketParams = Readonly<{
|
|
154
|
-
quote: Address;
|
|
155
|
-
subjectAmount: bigint;
|
|
156
|
-
quoteAmount: bigint;
|
|
157
|
-
lpFeeRecipient: Address;
|
|
158
|
-
tickSpacing: number;
|
|
159
|
-
sqrtPriceX96: bigint;
|
|
160
|
-
kernelId: Hex;
|
|
161
|
-
modules: readonly ModuleSelection[];
|
|
162
|
-
limits: StackLimits;
|
|
163
|
-
}>;
|
|
164
|
-
export type InitialBuyParams = Readonly<{
|
|
165
|
-
quoteAmountIn: bigint;
|
|
166
|
-
subjectAmountOutMinimum: bigint;
|
|
167
|
-
deadline: bigint;
|
|
168
|
-
moduleData: Hex;
|
|
169
|
-
}>;
|
|
170
|
-
export type NewTokenArgs = Readonly<{
|
|
171
|
-
name: string;
|
|
172
|
-
symbol: string;
|
|
173
|
-
tagline: string;
|
|
174
|
-
logoURI: string;
|
|
175
|
-
expectedCreator: Address;
|
|
176
|
-
totalSupply: bigint;
|
|
177
|
-
deploymentSalt: Hex;
|
|
178
|
-
market: MarketParams;
|
|
179
|
-
initialBuy: InitialBuyParams;
|
|
180
|
-
}>;
|
|
181
|
-
export type ExistingTokenArgs = Readonly<{
|
|
182
|
-
subject: Address;
|
|
183
|
-
market: MarketParams;
|
|
184
|
-
}>;
|
|
185
|
-
/** The rule numbers a market's stack carries, as the builders take them. */
|
|
186
|
-
export type MarketRulesInput = Readonly<{
|
|
187
|
-
/** The stack's Base LP fee, in pips. */
|
|
188
|
-
baseFeePips: number;
|
|
189
|
-
blocks: readonly RuleBlock[];
|
|
190
|
-
/** Guard length in contract blocks; 0 (default) turns the guard off. New-token lane only. */
|
|
191
|
-
guardBlocks?: number;
|
|
192
|
-
/** Per-block max buy in quote raw units while the guard is up. New-token lane only. */
|
|
193
|
-
maxBuyQuoteAmount?: bigint;
|
|
194
|
-
royaltyBps?: number;
|
|
195
|
-
/** Required exactly when `royaltyBps` is nonzero. */
|
|
196
|
-
royaltyTo?: Address | null;
|
|
197
|
-
}>;
|
|
198
|
-
export type CoordinatorCall<Args extends readonly unknown[]> = Readonly<{
|
|
199
|
-
address: Address;
|
|
200
|
-
abi: typeof hookrMarketCoordinatorV5Abi;
|
|
201
|
-
functionName: "openNewTokenMarket" | "openExistingTokenMarket";
|
|
202
|
-
args: Args;
|
|
203
|
-
value: bigint;
|
|
204
|
-
}>;
|
|
205
|
-
/**
|
|
206
|
-
* Build the exact `openNewTokenMarket` call: pool key/id from `subject` (the previewed token
|
|
207
|
-
* address — `marketCoordinator.previewNewTokenAddress`) and `quote`, the native config from
|
|
208
|
-
* `rules`, the one native-mechanics module, and the stack limits from `caps`. `value` is the
|
|
209
|
-
* dev buy's quote amount for a native-ETH quote, else 0 (the router pulls an ERC-20 dev buy from
|
|
210
|
-
* an allowance, exactly like the app).
|
|
211
|
-
*/
|
|
212
|
-
export declare function buildNewTokenMarketCall(input: {
|
|
213
|
-
creator: Address;
|
|
214
|
-
name: string;
|
|
215
|
-
symbol: string;
|
|
216
|
-
tagline: string;
|
|
217
|
-
logoURI: string;
|
|
218
|
-
totalSupply: bigint;
|
|
219
|
-
deploymentSalt: Hex;
|
|
220
|
-
intentId: Hex;
|
|
221
|
-
/** The coordinator's previewed token address for this intent; also the market's `subject`. */
|
|
222
|
-
expectedToken: Address;
|
|
223
|
-
quote: Address;
|
|
224
|
-
tickSpacing: number;
|
|
225
|
-
sqrtPriceX96: bigint;
|
|
226
|
-
/** Founding-position fee recipient; defaults to `creator`. */
|
|
227
|
-
lpFeeRecipient?: Address;
|
|
228
|
-
rules: MarketRulesInput;
|
|
229
|
-
/** The contract clock's block at build time (`context.contractBlock` in the app); anchors the guard. */
|
|
230
|
-
contractBlock: bigint;
|
|
231
|
-
protocolRecipient: Address;
|
|
232
|
-
protocolShareBps: number;
|
|
233
|
-
caps: StackCaps;
|
|
234
|
-
initialBuy?: Readonly<{
|
|
235
|
-
quoteAmountIn: bigint;
|
|
236
|
-
subjectAmountOutMinimum: bigint;
|
|
237
|
-
deadline: bigint;
|
|
238
|
-
moduleData?: Hex;
|
|
239
|
-
}>;
|
|
240
|
-
/** The root to open the market on: `ROOTS.default` when omitted, or `ROOTS.recapture`. */
|
|
241
|
-
root?: RootChoice;
|
|
242
|
-
}): CoordinatorCall<readonly [NewTokenArgs, Hex, Address]>;
|
|
243
|
-
/**
|
|
244
|
-
* Build the exact `openExistingTokenMarket` call. The existing-asset lane carries no guard, no
|
|
245
|
-
* founding position and no dev buy: `value` is always 0, `lpFeeRecipient` is always the zero
|
|
246
|
-
* address, and any guard field in `rules` is ignored (zeroed), exactly as the app's plan builder
|
|
247
|
-
* refuses a guard on this lane rather than silently applying it.
|
|
248
|
-
*/
|
|
249
|
-
export declare function buildExistingTokenMarketCall(input: {
|
|
250
|
-
subject: Address;
|
|
251
|
-
quote: Address;
|
|
252
|
-
tickSpacing: number;
|
|
253
|
-
sqrtPriceX96: bigint;
|
|
254
|
-
rules: MarketRulesInput;
|
|
255
|
-
protocolRecipient: Address;
|
|
256
|
-
protocolShareBps: number;
|
|
257
|
-
caps: StackCaps;
|
|
258
|
-
/** The root to open the market on: `ROOTS.default` when omitted, or `ROOTS.recapture`. */
|
|
259
|
-
root?: RootChoice;
|
|
260
|
-
/** The launching wallet; required on the recapture root, where it is frozen as the correction creator. */
|
|
261
|
-
creator?: Address;
|
|
262
|
-
}): CoordinatorCall<readonly [ExistingTokenArgs]>;
|
|
263
|
-
/** The calldata `encodeFunctionData` would produce for a built call, for sending with viem directly. */
|
|
264
|
-
export declare function encodeMarketCall(call: CoordinatorCall<readonly unknown[]>): Hex;
|
package/dist/market.js
DELETED
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pool key/id, the native block's config encoding, stack limits, and the two coordinator calls
|
|
3
|
-
* that open a market.
|
|
4
|
-
*
|
|
5
|
-
* `poolKeyFor`, `poolIdFor`, the config encoder and `stackLimitsFor` are pure, faithful copies
|
|
6
|
-
* of `src/lib/v2-encode.ts` in the Hookr app (`v2PoolKey`, `v2PoolId`, `encodeV2Config`,
|
|
7
|
-
* `v2StackLimits`) — nothing here reads a chain, so `src/test/hookr-sdk-parity.test.ts` proves
|
|
8
|
-
* them by calling both on the same fixed inputs and asserting byte-for-byte agreement.
|
|
9
|
-
*
|
|
10
|
-
* `buildNewTokenMarketCall` and `buildExistingTokenMarketCall` assemble the exact
|
|
11
|
-
* `openNewTokenMarket` / `openExistingTokenMarket` calldata `src/lib/v2-launch-execute.ts`
|
|
12
|
-
* produces from a reviewed plan (`v2ExecuteTransactions`), field for field with
|
|
13
|
-
* `src/lib/v2-launch-plan.ts`'s `buildV2LaunchPlan`. Parity reached, with one deliberate scope
|
|
14
|
-
* cut, stated here rather than left implicit:
|
|
15
|
-
*
|
|
16
|
-
* - `caps` (the native block's `validateStack` answer — its surcharge and take-bps ceilings)
|
|
17
|
-
* and `protocolShareBps` / `protocolRecipient` (the coordinator's and block's own reads for
|
|
18
|
-
* this creator) are NOT re-derived here: they are live chain reads in the app
|
|
19
|
-
* (`buildV2LaunchPlan` calls `validateStack`, `protocolShareBps`, and reads the block's
|
|
20
|
-
* `protocolRecipient()`), and this package does not carry a client of its own. Pass them in
|
|
21
|
-
* — read with viem's `readContract` against `abi.ts`'s ABIs — exactly as the app's plan
|
|
22
|
-
* builder does before it ever encodes a call.
|
|
23
|
-
* - The opening `sqrtPriceX96` (and, on the new-token lane, the founding band) is a caller
|
|
24
|
-
* input too. The app derives it from a chosen price via tick math
|
|
25
|
-
* (`v2SqrtPriceForOpeningPrice`, `v2FoundingBand`) that depends on Uniswap's `TickMath`
|
|
26
|
-
* library; reimplementing and re-verifying that library was out of this pass's time box, so
|
|
27
|
-
* it is not duplicated here. Compute it with the app's own `v2-encode.ts` (or an equivalent),
|
|
28
|
-
* or read it back from an already-opened pool with `reads.ts`.
|
|
29
|
-
* - The dev buy (`initialBuy`) is accepted as an already-priced input, not computed: sizing one
|
|
30
|
-
* against the founding band (`src/lib/v2-dev-buy.ts`) is a further, separate pricing
|
|
31
|
-
* concern this pass did not reach. Omit it for a plain launch with no creator buy.
|
|
32
|
-
*
|
|
33
|
-
* Everything downstream of those three reads — the pool id, the config bytes, the stack limits,
|
|
34
|
-
* and the final struct-for-struct call — is exact, proven, and the parity test's fixed-input
|
|
35
|
-
* cases exercise the full assembly with a representative `caps`/`sqrtPriceX96` pair.
|
|
36
|
-
*/
|
|
37
|
-
import { encodeAbiParameters, encodeFunctionData, keccak256 } from "viem";
|
|
38
|
-
import { hookrMarketCoordinatorV5Abi } from "./abi.js";
|
|
39
|
-
import { ADDRESSES, MODULE_ID, NATIVE_ADDRESS, RECAPTURE_CORRECTION, ROOTS, isRecaptureRoot } from "./chain.js";
|
|
40
|
-
import { compileRuleBlocks } from "./rules.js";
|
|
41
|
-
// -----------------------------------------------------------------------------------------------
|
|
42
|
-
// Pool key and pool id
|
|
43
|
-
// -----------------------------------------------------------------------------------------------
|
|
44
|
-
/** `HookrMarketCoordinatorV5.DYNAMIC_FEE_FLAG`: the Uniswap v4 dynamic-fee sentinel in every V2 PoolKey. */
|
|
45
|
-
export const DYNAMIC_FEE = 0x800000;
|
|
46
|
-
const POOL_KEY_COMPONENTS = [
|
|
47
|
-
{ name: "currency0", type: "address" },
|
|
48
|
-
{ name: "currency1", type: "address" },
|
|
49
|
-
{ name: "fee", type: "uint24" },
|
|
50
|
-
{ name: "tickSpacing", type: "int24" },
|
|
51
|
-
{ name: "hooks", type: "address" },
|
|
52
|
-
];
|
|
53
|
-
const POOL_KEY_ABI = [{ type: "tuple", components: POOL_KEY_COMPONENTS }];
|
|
54
|
-
/** `subject < quote` by numeric value; ETH (the zero address) is therefore never `currency1`. */
|
|
55
|
-
export function subjectIsCurrency0(subject, quote) {
|
|
56
|
-
const s = BigInt(subject);
|
|
57
|
-
const q = BigInt(quote);
|
|
58
|
-
if (s === q)
|
|
59
|
-
throw new Error("The subject and the quote are the same currency.");
|
|
60
|
-
return s < q;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* `HookrMarketCoordinatorV5._poolKey`: currencies sorted numerically, `fee = DYNAMIC_FEE`,
|
|
64
|
-
* `hooks` the root hook: `ADDRESSES.root` (the default root) unless a pool on another root is
|
|
65
|
-
* being addressed, in which case pass that root's address (a market row's `kernel`).
|
|
66
|
-
*/
|
|
67
|
-
export function poolKeyFor(input) {
|
|
68
|
-
const subjectFirst = subjectIsCurrency0(input.subject, input.quote);
|
|
69
|
-
return Object.freeze({
|
|
70
|
-
currency0: subjectFirst ? input.subject : input.quote,
|
|
71
|
-
currency1: subjectFirst ? input.quote : input.subject,
|
|
72
|
-
fee: DYNAMIC_FEE,
|
|
73
|
-
tickSpacing: input.tickSpacing,
|
|
74
|
-
hooks: input.hooks ?? ADDRESSES.root,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
/** `PoolId = keccak256(abi.encode(key))`. */
|
|
78
|
-
export function poolIdFor(key) {
|
|
79
|
-
return keccak256(encodeAbiParameters(POOL_KEY_ABI, [key]));
|
|
80
|
-
}
|
|
81
|
-
// -----------------------------------------------------------------------------------------------
|
|
82
|
-
// HookrNativeMechanicsBlockV2.Config
|
|
83
|
-
// -----------------------------------------------------------------------------------------------
|
|
84
|
-
/** The 20 fields of `HookrNativeMechanicsBlockV2.Config`, in the struct's own order. */
|
|
85
|
-
const CONFIG_COMPONENTS = [
|
|
86
|
-
{ name: "poolId", type: "bytes32" },
|
|
87
|
-
{ name: "kernel", type: "address" },
|
|
88
|
-
{ name: "subject", type: "address" },
|
|
89
|
-
{ name: "quote", type: "address" },
|
|
90
|
-
{ name: "lockedLiquidityProvider", type: "address" },
|
|
91
|
-
{ name: "guardEndBlock", type: "uint40" },
|
|
92
|
-
{ name: "baseFeePips", type: "uint24" },
|
|
93
|
-
{ name: "maxFeePips", type: "uint24" },
|
|
94
|
-
{ name: "snipeTaxPips", type: "uint24" },
|
|
95
|
-
{ name: "surgeSens", type: "uint16" },
|
|
96
|
-
{ name: "burnBps", type: "uint16" },
|
|
97
|
-
{ name: "lpBps", type: "uint16" },
|
|
98
|
-
{ name: "potBps", type: "uint16" },
|
|
99
|
-
{ name: "royaltyBps", type: "uint16" },
|
|
100
|
-
{ name: "potEveryNBuys", type: "uint32" },
|
|
101
|
-
{ name: "maxBuyQuoteAmount", type: "uint96" },
|
|
102
|
-
{ name: "potMinBuyWei", type: "uint96" },
|
|
103
|
-
{ name: "royaltyTo", type: "address" },
|
|
104
|
-
{ name: "protocolRecipient", type: "address" },
|
|
105
|
-
{ name: "protocolShareBps", type: "uint24" },
|
|
106
|
-
];
|
|
107
|
-
const CONFIG_ABI = [{ type: "tuple", components: CONFIG_COMPONENTS }];
|
|
108
|
-
/** `abi.encode(Config)`: one static tuple, exactly 640 bytes. */
|
|
109
|
-
export function encodeNativeBlockConfig(config) {
|
|
110
|
-
return encodeAbiParameters(CONFIG_ABI, [{ ...config, guardEndBlock: Number(config.guardEndBlock) }]);
|
|
111
|
-
}
|
|
112
|
-
/** `HookrNativeMechanicsBlockV2.MAX_GUARD_BLOCKS`. */
|
|
113
|
-
export const MAX_GUARD_BLOCKS = 100_000;
|
|
114
|
-
/** `guardEndBlock` for a guard of `guardBlocks` blocks from `contractBlock`; `0n` (off) at or below zero. */
|
|
115
|
-
export function guardEndBlockFor(contractBlock, guardBlocks) {
|
|
116
|
-
if (guardBlocks <= 0)
|
|
117
|
-
return 0n;
|
|
118
|
-
return contractBlock + BigInt(Math.min(Math.round(guardBlocks), MAX_GUARD_BLOCKS));
|
|
119
|
-
}
|
|
120
|
-
// -----------------------------------------------------------------------------------------------
|
|
121
|
-
// Stack limits
|
|
122
|
-
// -----------------------------------------------------------------------------------------------
|
|
123
|
-
/** The native block's registered callback gas; `limits.maxTotalModuleGas` in every fixture. */
|
|
124
|
-
export const MODULE_GAS = 2_000_000;
|
|
125
|
-
export const ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
126
|
-
/**
|
|
127
|
-
* The correction wiring a market on `root` must freeze, or null on a root that does not correct.
|
|
128
|
-
* The registry admits a correction executor only on a native-ETH-quoted pool and requires a
|
|
129
|
-
* non-zero creator, so both are checked here rather than failing on chain.
|
|
130
|
-
*/
|
|
131
|
-
export function correctionFor(root, quote, creator) {
|
|
132
|
-
if (!isRecaptureRoot(root.hook))
|
|
133
|
-
return null;
|
|
134
|
-
if (quote.toLowerCase() !== NATIVE_ADDRESS.toLowerCase()) {
|
|
135
|
-
throw new Error("The recapture root corrects native-ETH-quoted pools only; the registry rejects any other quote.");
|
|
136
|
-
}
|
|
137
|
-
if (!creator || creator.toLowerCase() === NATIVE_ADDRESS.toLowerCase()) {
|
|
138
|
-
throw new Error("Opening a market on the recapture root needs the creator wallet; it is frozen as the correction creator.");
|
|
139
|
-
}
|
|
140
|
-
return Object.freeze({ ...RECAPTURE_CORRECTION, creator });
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* The tightest `StackLimits` the registry admits: equal to the block's own caps, exactly as the
|
|
144
|
-
* app's `v2StackLimits` sets them, with the correction lane frozen in when `correction` is given. `trustedRouter`/`trustedQuoter` are always `ADDRESSES.router`
|
|
145
|
-
* / `ADDRESSES.quoter` today — the app's own equivalent takes them as a parameter only so its
|
|
146
|
-
* tests can fake them; a real launch always wires the same two addresses this package does.
|
|
147
|
-
*/
|
|
148
|
-
export function stackLimitsFor(config, caps, correction = null) {
|
|
149
|
-
return Object.freeze({
|
|
150
|
-
baseLpFeePips: config.baseFeePips,
|
|
151
|
-
maxLpFeePips: config.baseFeePips + caps.maxLpFeeSurchargePips,
|
|
152
|
-
maxSpecifiedQuoteTakeBps: caps.maxSpecifiedQuoteTakeBps,
|
|
153
|
-
maxUnspecifiedQuoteTakeBps: caps.maxUnspecifiedQuoteTakeBps,
|
|
154
|
-
maxSubjectTakeBps: caps.maxSubjectTakeBps,
|
|
155
|
-
maxTotalModuleGas: MODULE_GAS,
|
|
156
|
-
trustedRouter: ADDRESSES.router,
|
|
157
|
-
trustedQuoter: ADDRESSES.quoter,
|
|
158
|
-
correctionExecutor: correction?.executor ?? NATIVE_ADDRESS,
|
|
159
|
-
correctionCreator: correction?.creator ?? NATIVE_ADDRESS,
|
|
160
|
-
correctionMaxVolumeBps: correction?.maxVolumeBps ?? 0,
|
|
161
|
-
correctionMinProfitQuote: correction?.minProfitQuote ?? 0n,
|
|
162
|
-
correctionFeePolicyId: correction?.feePolicyId ?? ZERO_BYTES32,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
const NO_INITIAL_BUY = Object.freeze({
|
|
166
|
-
quoteAmountIn: 0n,
|
|
167
|
-
subjectAmountOutMinimum: 0n,
|
|
168
|
-
deadline: 0n,
|
|
169
|
-
moduleData: "0x",
|
|
170
|
-
});
|
|
171
|
-
function buildNativeConfig(input) {
|
|
172
|
-
const { lane, rules } = input;
|
|
173
|
-
const compiled = compileRuleBlocks(rules.blocks, rules.baseFeePips);
|
|
174
|
-
const guardOn = lane === "new" && (rules.guardBlocks ?? 0) > 0;
|
|
175
|
-
return Object.freeze({
|
|
176
|
-
poolId: input.poolId,
|
|
177
|
-
kernel: input.kernel ?? ADDRESSES.root,
|
|
178
|
-
subject: input.subject,
|
|
179
|
-
quote: input.quote,
|
|
180
|
-
lockedLiquidityProvider: guardOn ? ADDRESSES.coordinator : NATIVE_ADDRESS,
|
|
181
|
-
guardEndBlock: guardOn ? guardEndBlockFor(input.contractBlock, compiled.guardBlocks) : 0n,
|
|
182
|
-
baseFeePips: compiled.baseFeePips,
|
|
183
|
-
maxFeePips: compiled.maxFeePips,
|
|
184
|
-
snipeTaxPips: lane === "new" ? compiled.snipeTaxPips : 0,
|
|
185
|
-
surgeSens: compiled.surgeSens,
|
|
186
|
-
burnBps: compiled.burnBps,
|
|
187
|
-
lpBps: compiled.lpBps,
|
|
188
|
-
potBps: compiled.potBps,
|
|
189
|
-
royaltyBps: rules.royaltyBps ?? 0,
|
|
190
|
-
potEveryNBuys: compiled.potEveryNBuys,
|
|
191
|
-
maxBuyQuoteAmount: lane === "new" ? rules.maxBuyQuoteAmount ?? 0n : 0n,
|
|
192
|
-
potMinBuyWei: compiled.potMinBuyWei,
|
|
193
|
-
royaltyTo: rules.royaltyTo ?? NATIVE_ADDRESS,
|
|
194
|
-
protocolRecipient: input.protocolRecipient,
|
|
195
|
-
protocolShareBps: input.protocolShareBps,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Build the exact `openNewTokenMarket` call: pool key/id from `subject` (the previewed token
|
|
200
|
-
* address — `marketCoordinator.previewNewTokenAddress`) and `quote`, the native config from
|
|
201
|
-
* `rules`, the one native-mechanics module, and the stack limits from `caps`. `value` is the
|
|
202
|
-
* dev buy's quote amount for a native-ETH quote, else 0 (the router pulls an ERC-20 dev buy from
|
|
203
|
-
* an allowance, exactly like the app).
|
|
204
|
-
*/
|
|
205
|
-
export function buildNewTokenMarketCall(input) {
|
|
206
|
-
const root = input.root ?? ROOTS.default;
|
|
207
|
-
const subject = input.expectedToken;
|
|
208
|
-
const poolId = poolIdFor(poolKeyFor({ subject, quote: input.quote, tickSpacing: input.tickSpacing, hooks: root.hook }));
|
|
209
|
-
const config = buildNativeConfig({
|
|
210
|
-
lane: "new",
|
|
211
|
-
kernel: root.hook,
|
|
212
|
-
rules: input.rules,
|
|
213
|
-
poolId,
|
|
214
|
-
subject,
|
|
215
|
-
quote: input.quote,
|
|
216
|
-
contractBlock: input.contractBlock,
|
|
217
|
-
protocolRecipient: input.protocolRecipient,
|
|
218
|
-
protocolShareBps: input.protocolShareBps,
|
|
219
|
-
});
|
|
220
|
-
const encodedConfig = encodeNativeBlockConfig(config);
|
|
221
|
-
const limits = stackLimitsFor(config, input.caps, correctionFor(root, input.quote, input.creator));
|
|
222
|
-
const market = {
|
|
223
|
-
quote: input.quote,
|
|
224
|
-
subjectAmount: input.totalSupply,
|
|
225
|
-
quoteAmount: 0n,
|
|
226
|
-
lpFeeRecipient: input.lpFeeRecipient ?? input.creator,
|
|
227
|
-
tickSpacing: input.tickSpacing,
|
|
228
|
-
sqrtPriceX96: input.sqrtPriceX96,
|
|
229
|
-
kernelId: root.kernelId,
|
|
230
|
-
modules: [{ moduleId: MODULE_ID, config: encodedConfig }],
|
|
231
|
-
limits,
|
|
232
|
-
};
|
|
233
|
-
const initialBuy = input.initialBuy
|
|
234
|
-
? {
|
|
235
|
-
quoteAmountIn: input.initialBuy.quoteAmountIn,
|
|
236
|
-
subjectAmountOutMinimum: input.initialBuy.subjectAmountOutMinimum,
|
|
237
|
-
deadline: input.initialBuy.deadline,
|
|
238
|
-
moduleData: input.initialBuy.moduleData ?? "0x",
|
|
239
|
-
}
|
|
240
|
-
: NO_INITIAL_BUY;
|
|
241
|
-
const args = {
|
|
242
|
-
name: input.name,
|
|
243
|
-
symbol: input.symbol,
|
|
244
|
-
tagline: input.tagline,
|
|
245
|
-
logoURI: input.logoURI,
|
|
246
|
-
expectedCreator: input.creator,
|
|
247
|
-
totalSupply: input.totalSupply,
|
|
248
|
-
deploymentSalt: input.deploymentSalt,
|
|
249
|
-
market,
|
|
250
|
-
initialBuy,
|
|
251
|
-
};
|
|
252
|
-
const value = input.quote === NATIVE_ADDRESS ? initialBuy.quoteAmountIn : 0n;
|
|
253
|
-
return Object.freeze({
|
|
254
|
-
address: ADDRESSES.coordinator,
|
|
255
|
-
abi: hookrMarketCoordinatorV5Abi,
|
|
256
|
-
functionName: "openNewTokenMarket",
|
|
257
|
-
args: [args, input.intentId, input.expectedToken],
|
|
258
|
-
value,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Build the exact `openExistingTokenMarket` call. The existing-asset lane carries no guard, no
|
|
263
|
-
* founding position and no dev buy: `value` is always 0, `lpFeeRecipient` is always the zero
|
|
264
|
-
* address, and any guard field in `rules` is ignored (zeroed), exactly as the app's plan builder
|
|
265
|
-
* refuses a guard on this lane rather than silently applying it.
|
|
266
|
-
*/
|
|
267
|
-
export function buildExistingTokenMarketCall(input) {
|
|
268
|
-
const root = input.root ?? ROOTS.default;
|
|
269
|
-
const poolId = poolIdFor(poolKeyFor({ subject: input.subject, quote: input.quote, tickSpacing: input.tickSpacing, hooks: root.hook }));
|
|
270
|
-
const config = buildNativeConfig({
|
|
271
|
-
lane: "existing",
|
|
272
|
-
kernel: root.hook,
|
|
273
|
-
rules: input.rules,
|
|
274
|
-
poolId,
|
|
275
|
-
subject: input.subject,
|
|
276
|
-
quote: input.quote,
|
|
277
|
-
contractBlock: 0n,
|
|
278
|
-
protocolRecipient: input.protocolRecipient,
|
|
279
|
-
protocolShareBps: input.protocolShareBps,
|
|
280
|
-
});
|
|
281
|
-
const encodedConfig = encodeNativeBlockConfig(config);
|
|
282
|
-
const limits = stackLimitsFor(config, input.caps, correctionFor(root, input.quote, input.creator));
|
|
283
|
-
const market = {
|
|
284
|
-
quote: input.quote,
|
|
285
|
-
subjectAmount: 0n,
|
|
286
|
-
quoteAmount: 0n,
|
|
287
|
-
lpFeeRecipient: NATIVE_ADDRESS,
|
|
288
|
-
tickSpacing: input.tickSpacing,
|
|
289
|
-
sqrtPriceX96: input.sqrtPriceX96,
|
|
290
|
-
kernelId: root.kernelId,
|
|
291
|
-
modules: [{ moduleId: MODULE_ID, config: encodedConfig }],
|
|
292
|
-
limits,
|
|
293
|
-
};
|
|
294
|
-
const args = { subject: input.subject, market };
|
|
295
|
-
return Object.freeze({
|
|
296
|
-
address: ADDRESSES.coordinator,
|
|
297
|
-
abi: hookrMarketCoordinatorV5Abi,
|
|
298
|
-
functionName: "openExistingTokenMarket",
|
|
299
|
-
args: [args],
|
|
300
|
-
value: 0n,
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
/** The calldata `encodeFunctionData` would produce for a built call, for sending with viem directly. */
|
|
304
|
-
export function encodeMarketCall(call) {
|
|
305
|
-
return encodeFunctionData({
|
|
306
|
-
abi: call.abi,
|
|
307
|
-
functionName: call.functionName,
|
|
308
|
-
args: call.args,
|
|
309
|
-
});
|
|
310
|
-
}
|