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
@@ -0,0 +1,378 @@
1
+ import { decodeAbiParameters, decodeFunctionResult, encodeAbiParameters, encodeFunctionData, keccak256, parseAbi, toBytes, } from "viem";
2
+ import { ARTIFACT_ABIS, RELEASE_CONSTANTS } from "./generated.js";
3
+ import { address, assert, hash, uint, ZERO_HASH, } from "./schema.js";
4
+ import { MAX_LP_FEE_PIPS, RULES_GAS_LIMIT } from "./rules.js";
5
+ import { buildCall } from "./wallet.js";
6
+ const registryAbi = ARTIFACT_ABIS.HookrRegistry;
7
+ /** IHookrRegistry.Kind. */
8
+ export const ADMISSION_KIND = { RULES: 0, ADVISORY: 1 };
9
+ /** HookrTypes phase bits an advisory may run in. */
10
+ export const PHASE = { BEFORE_SWAP: 1, AFTER_SWAP: 2 };
11
+ /**
12
+ * The quote-admission surface of HookrRegistry: the two timelocked operations (a quote class by
13
+ * runtime codehash, open quotes), their two immediate brakes (owner or guardian), the reads and the events. Written
14
+ * from its compiled ABI; `test/quote-admission.test.ts` pins every item to it (`test/fixtures`) and to the
15
+ * generated ABI once the packet carries them. Their kind constants are private in the registry: keccak256 of the name.
16
+ */
17
+ export const QUOTE_ADMISSION_ABI = parseAbi([
18
+ "function enableQuoteClassOf(address witness, bytes32 codeHash)",
19
+ "function excludeQuoteClassNow(bytes32 codeHash)",
20
+ "function allowOpenQuotes()",
21
+ "function stopOpenQuotesNow()",
22
+ "function curatedQuote(address asset) view returns (bool)",
23
+ "function quoteState() view returns (bool openQuotes, uint48 openQuotesStoppedAt, bytes32[] classes)",
24
+ "event QuoteClassSet(bytes32 indexed codeHash, address indexed witness, bool active)",
25
+ "event QuoteClassExcluded(bytes32 indexed codeHash, address indexed by)",
26
+ "event OpenQuotesSet(bool open, address indexed by)",
27
+ "error QuoteClassActive(bytes32 codeHash)",
28
+ "error OpenQuotesActive()",
29
+ "error QueuedBeforeBrake(bytes32 operation)",
30
+ ]);
31
+ /** The two quote-admission operations by kind name, and the function each executes with. */
32
+ export const QUOTE_ADMISSION_OPERATIONS = {
33
+ SET_QUOTE_CLASS: "enableQuoteClassOf",
34
+ OPEN_QUOTES: "allowOpenQuotes",
35
+ };
36
+ const params = (ps = []) => ps
37
+ .map((p) => (p.type.startsWith("tuple") ? `(${params(p.components)})${p.type.slice(5)}` : p.type) +
38
+ (p.indexed ? " indexed" : ""))
39
+ .join(",");
40
+ /** An ABI item's full signature, names aside: kind, name, parameter types, indexed flags, mutability and outputs. */
41
+ function signature(item) {
42
+ const e = item;
43
+ const head = `${e.type} ${e.name ?? ""}(${params(e.inputs)})`;
44
+ return e.type === "function" ? `${head} ${e.stateMutability} returns (${params(e.outputs)})` : head;
45
+ }
46
+ /**
47
+ * Whether `abi` (by default the packet's generated HookrRegistry ABI) carries every quote-admission item with the exact
48
+ * signature. The operations, brakes and reads below refuse an ABI without them, so nothing can build a call the
49
+ * release does not have; a caller that targets another registry build passes that build's ABI.
50
+ */
51
+ export function registryHasQuoteAdmission(abi = registryAbi) {
52
+ const have = new Set(abi.map(signature));
53
+ return QUOTE_ADMISSION_ABI.every((item) => have.has(signature(item)));
54
+ }
55
+ /** Whether `abi` carries the function a quote-admission operation executes with. */
56
+ export function registryHasOperation(kind, abi = registryAbi) {
57
+ const item = QUOTE_ADMISSION_ABI.find((e) => e.type === "function" && e.name === QUOTE_ADMISSION_OPERATIONS[kind]);
58
+ return abi.some((e) => signature(e) === signature(item));
59
+ }
60
+ const kindHash = (name) => keccak256(toBytes(name));
61
+ /** keccak256 of empty code: an account without code has it, so no class may be admitted by it. */
62
+ const EMPTY_CODE_HASH = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470";
63
+ /** A quote class's runtime codehash: nonzero and never the empty-code hash. */
64
+ export function quoteClassHash(value) {
65
+ const h = hash(value);
66
+ assert(h !== ZERO_HASH && h !== EMPTY_CODE_HASH, "A quote class needs a real runtime codehash");
67
+ return h;
68
+ }
69
+ /** keccak256 of each kind name, as the registry's public constants. */
70
+ export const REGISTRY_KINDS = {
71
+ SET_LAUNCHER: RELEASE_CONSTANTS.SET_LAUNCHER,
72
+ SET_QUOTE: RELEASE_CONSTANTS.SET_QUOTE,
73
+ SET_QUOTE_CLASS: kindHash("SET_QUOTE_CLASS"),
74
+ OPEN_QUOTES: kindHash("OPEN_QUOTES"),
75
+ REGISTER_ROOT: RELEASE_CONSTANTS.REGISTER_ROOT,
76
+ RETIRE_ROOT: RELEASE_CONSTANTS.RETIRE_ROOT,
77
+ REOPEN_ROOT: RELEASE_CONSTANTS.REOPEN_ROOT,
78
+ SET_ROOT_FACTORY: RELEASE_CONSTANTS.SET_ROOT_FACTORY,
79
+ ADMIT: RELEASE_CONSTANTS.ADMIT,
80
+ TRANSFER_OWNER: RELEASE_CONSTANTS.TRANSFER_OWNER,
81
+ SET_GUARDIAN: RELEASE_CONSTANTS.SET_GUARDIAN,
82
+ RESUME_NEW_MARKETS: RELEASE_CONSTANTS.RESUME_NEW_MARKETS,
83
+ };
84
+ function inputs(name, abi = registryAbi) {
85
+ const entry = abi.find((e) => e.type === "function" && e.name === name);
86
+ assert(entry && entry.type === "function", `HookrRegistry ABI lacks ${name}`);
87
+ const pinned = QUOTE_ADMISSION_ABI.find((e) => e.type === "function" && e.name === name);
88
+ assert(!pinned || signature(pinned) === signature(entry), `HookrRegistry ABI differs for ${name}`);
89
+ return entry.inputs;
90
+ }
91
+ /** Each kind's executing function and the ABI arguments it re-derives, taken from the artifact. */
92
+ const EXECUTE = {
93
+ SET_LAUNCHER: "setLauncher",
94
+ SET_QUOTE: "setQuote",
95
+ SET_QUOTE_CLASS: QUOTE_ADMISSION_OPERATIONS.SET_QUOTE_CLASS,
96
+ OPEN_QUOTES: QUOTE_ADMISSION_OPERATIONS.OPEN_QUOTES,
97
+ REGISTER_ROOT: "registerRoot",
98
+ RETIRE_ROOT: "retireRoot",
99
+ REOPEN_ROOT: "reopenRoot",
100
+ SET_ROOT_FACTORY: "setRootFactory",
101
+ ADMIT: "admit",
102
+ TRANSFER_OWNER: "transferOwnership",
103
+ SET_GUARDIAN: "setGuardian",
104
+ RESUME_NEW_MARKETS: "resumeNewMarkets",
105
+ };
106
+ function values(op) {
107
+ switch (op.kind) {
108
+ case "SET_LAUNCHER":
109
+ return [address(op.launcher), op.active];
110
+ case "SET_QUOTE":
111
+ return [address(op.asset), op.active];
112
+ case "SET_QUOTE_CLASS":
113
+ return [address(op.witness), quoteClassHash(op.codeHash)];
114
+ case "OPEN_QUOTES":
115
+ return [];
116
+ case "REGISTER_ROOT":
117
+ assert(hash(op.codeHash) !== ZERO_HASH, "Missing root codehash");
118
+ return [address(op.root), op.codeHash];
119
+ case "RETIRE_ROOT":
120
+ case "REOPEN_ROOT":
121
+ return [address(op.root)];
122
+ case "SET_ROOT_FACTORY":
123
+ assert(!op.active || hash(op.codeHash) !== ZERO_HASH, "Missing factory codehash");
124
+ return [address(op.factory), hash(op.codeHash), op.active];
125
+ case "ADMIT":
126
+ checkAdmission(op.admission);
127
+ return [address(op.root), op.admission];
128
+ case "TRANSFER_OWNER":
129
+ return [address(op.nextOwner)];
130
+ case "SET_GUARDIAN":
131
+ return [address(op.guardian, true)];
132
+ case "RESUME_NEW_MARKETS":
133
+ return [];
134
+ }
135
+ }
136
+ /**
137
+ * `queue(kind, arguments)` inputs for an operation, with the canonical ABI arguments. `abi` is the registry build's ABI
138
+ * (default: the packet's); a kind whose executing function it lacks is refused.
139
+ */
140
+ export function encodeRegistryOperation(op, abi = registryAbi) {
141
+ const args = values(op);
142
+ const params = inputs(EXECUTE[op.kind], abi);
143
+ return {
144
+ kind: REGISTRY_KINDS[op.kind],
145
+ arguments: args.length ? encodeAbiParameters(params, args) : "0x",
146
+ };
147
+ }
148
+ /**
149
+ * Decodes an `OperationQueued(kind, arguments)` into the operation it will execute. Refuses an unknown kind and any
150
+ * non-canonical argument encoding, as HookrRegistry.queue does.
151
+ */
152
+ export function decodeRegistryOperation(kind, args, abi = registryAbi) {
153
+ const entry = Object.entries(REGISTRY_KINDS).find(([, k]) => k === kind.toLowerCase());
154
+ assert(entry, "Unknown registry operation kind");
155
+ const name = entry[0];
156
+ const params = inputs(EXECUTE[name], abi);
157
+ const v = params.length ? decodeAbiParameters(params, args) : [];
158
+ const lower = (a) => a.toLowerCase();
159
+ let op;
160
+ switch (name) {
161
+ case "SET_LAUNCHER":
162
+ op = { kind: name, launcher: lower(v[0]), active: v[1] };
163
+ break;
164
+ case "SET_QUOTE":
165
+ op = { kind: name, asset: lower(v[0]), active: v[1] };
166
+ break;
167
+ case "SET_QUOTE_CLASS":
168
+ op = { kind: name, witness: lower(v[0]), codeHash: v[1].toLowerCase() };
169
+ break;
170
+ case "OPEN_QUOTES":
171
+ op = { kind: name };
172
+ break;
173
+ case "REGISTER_ROOT":
174
+ op = { kind: name, root: lower(v[0]), codeHash: v[1] };
175
+ break;
176
+ case "RETIRE_ROOT":
177
+ case "REOPEN_ROOT":
178
+ op = { kind: name, root: lower(v[0]) };
179
+ break;
180
+ case "SET_ROOT_FACTORY":
181
+ op = { kind: name, factory: lower(v[0]), codeHash: v[1], active: v[2] };
182
+ break;
183
+ case "ADMIT":
184
+ op = { kind: name, root: lower(v[0]), admission: { ...v[1], implementation: lower(v[1].implementation) } };
185
+ break;
186
+ case "TRANSFER_OWNER":
187
+ op = { kind: name, nextOwner: lower(v[0]) };
188
+ break;
189
+ case "SET_GUARDIAN":
190
+ op = { kind: name, guardian: lower(v[0]) };
191
+ break;
192
+ case "RESUME_NEW_MARKETS":
193
+ op = { kind: name };
194
+ break;
195
+ }
196
+ assert(encodeAbiParameters(params, params.length ? values(op) : []).toLowerCase() === args.toLowerCase(), "Non-canonical registry operation arguments");
197
+ return op;
198
+ }
199
+ /** HookrRegistry.operationHash: keccak256(abi.encode(chainId, registry, kind, arguments)). */
200
+ export function registryOperationHash(chainId, registry, kind, args) {
201
+ return keccak256(encodeAbiParameters([{ type: "uint256" }, { type: "address" }, { type: "bytes32" }, { type: "bytes" }], [uint(chainId), address(registry), hash(kind), args]));
202
+ }
203
+ /** Owner call that queues `op`; it executes after the registry delay with `buildRegistryExecute`. */
204
+ export function buildRegistryQueue(m, op, abi = registryAbi) {
205
+ const { kind, arguments: args } = encodeRegistryOperation(op, abi);
206
+ return buildCall(m, "registry", abi, "queue", [kind, args]);
207
+ }
208
+ /** Owner call that executes a matured `op`: the same arguments the queue committed to. */
209
+ export function buildRegistryExecute(m, op, abi = registryAbi) {
210
+ inputs(EXECUTE[op.kind], abi);
211
+ return buildCall(m, "registry", abi, EXECUTE[op.kind], values(op));
212
+ }
213
+ function quoteAdmissionAbi(abi) {
214
+ assert(registryHasQuoteAdmission(abi), "HookrRegistry ABI lacks the quote-admission calls");
215
+ return abi;
216
+ }
217
+ /**
218
+ * Brake (owner or guardian, immediate): withdraws quote class `codeHash` from new pools and refuses every admission of
219
+ * it queued until now. Existing pools keep the quote they bound; a catalog entry for the same asset still qualifies it.
220
+ */
221
+ export function buildExcludeQuoteClass(m, codeHash, abi = registryAbi) {
222
+ return buildCall(m, "registry", quoteAdmissionAbi(abi), "excludeQuoteClassNow", [quoteClassHash(codeHash)]);
223
+ }
224
+ /**
225
+ * Brake (owner or guardian, immediate): turns open quotes off for new pools and refuses every opening queued until
226
+ * now. The catalog, the admitted classes and native ETH still qualify; existing pools keep their quote.
227
+ */
228
+ export function buildStopOpenQuotes(m, abi = registryAbi) {
229
+ return buildCall(m, "registry", quoteAdmissionAbi(abi), "stopOpenQuotesNow", []);
230
+ }
231
+ /** `quoteState()` calldata. */
232
+ export function encodeQuoteStateCall() {
233
+ return encodeFunctionData({ abi: QUOTE_ADMISSION_ABI, functionName: "quoteState" });
234
+ }
235
+ /** Decodes a `quoteState()` result. */
236
+ export function decodeQuoteState(data) {
237
+ const [openQuotes, stoppedAt, classes] = decodeFunctionResult({
238
+ abi: QUOTE_ADMISSION_ABI,
239
+ functionName: "quoteState",
240
+ data,
241
+ });
242
+ return {
243
+ openQuotes,
244
+ openQuotesStoppedAt: Number(stoppedAt),
245
+ classes: classes.map((c) => c.toLowerCase()),
246
+ };
247
+ }
248
+ /** `curatedQuote(asset)` calldata: native ETH, a catalog asset or a class member, never an open quote. */
249
+ export function encodeCuratedQuoteCall(asset) {
250
+ return encodeFunctionData({ abi: QUOTE_ADMISSION_ABI, functionName: "curatedQuote", args: [address(asset, true)] });
251
+ }
252
+ /** Decodes a `curatedQuote(asset)` result. */
253
+ export function decodeCuratedQuote(data) {
254
+ return decodeFunctionResult({ abi: QUOTE_ADMISSION_ABI, functionName: "curatedQuote", data });
255
+ }
256
+ /**
257
+ * Refuses an admission HookrRegistry would refuse on its own terms (the checks that do not read chain state).
258
+ * A Rules admission runs both phases, never fails open and carries exactly `RULES_GAS_LIMIT` gas.
259
+ */
260
+ export function checkAdmission(a) {
261
+ assert(a.kind === 0 || a.kind === 1, "Invalid admission kind");
262
+ address(a.implementation);
263
+ assert(hash(a.codeHash) !== ZERO_HASH, "Missing module codehash");
264
+ assert(hash(a.schemaHash) !== ZERO_HASH, "Missing schema hash");
265
+ assert(Number.isInteger(a.gasLimit) && a.gasLimit >= 25_000 && a.gasLimit <= RULES_GAS_LIMIT, "Admission gas out of range");
266
+ assert(Number.isInteger(a.caps.maxLpFeePips) && a.caps.maxLpFeePips >= 0 && a.caps.maxLpFeePips <= MAX_LP_FEE_PIPS, "Admission LP cap above 600000 pips");
267
+ assert(Number.isInteger(a.caps.maxQuoteTakePips) && a.caps.maxQuoteTakePips >= 0 && a.caps.maxQuoteTakePips < 1_000_000, "Admission quote take cap at or above 1000000 pips");
268
+ assert(Number.isInteger(a.caps.maxSubjectTakeBps) && a.caps.maxSubjectTakeBps >= 0 && a.caps.maxSubjectTakeBps <= 1_000, "Admission subject cap above 1000 bps");
269
+ assert(Number.isInteger(a.phaseMask) && (a.phaseMask & ~3) === 0, "Invalid phase mask");
270
+ if (a.kind === ADMISSION_KIND.RULES)
271
+ assert(a.phaseMask === 3 && !a.failOpen && a.gasLimit === RULES_GAS_LIMIT, "Rules admission must run both phases, fail closed and carry exactly 2000000 gas");
272
+ else
273
+ assert(a.phaseMask !== 0 && a.caps.maxSubjectTakeBps === 0, "Advisory admission needs a phase and no subject take");
274
+ if (a.failOpen)
275
+ assert(a.feeOnly && a.caps.maxQuoteTakePips === 0, "Fail-open admission must be fee-only");
276
+ }
277
+ /**
278
+ * A HookrRules admission: both phases, fail-closed, exactly RULES_GAS_LIMIT gas, and the Rules' schema id
279
+ * (`schemaHash`, RULES_CONFIG_SCHEMA_HASH, the keccak256 of RulesConfig's type string since r26; omitted, it is that
280
+ * id). Any other id is refused: HookrLane checks each admission's schemaHash against the Rules' own
281
+ * configSchemaHash(), so a Rules admitted under another id could never serve a lane pool.
282
+ */
283
+ export function rulesAdmission(p) {
284
+ const schemaHash = hash(p.schemaHash ?? RELEASE_CONSTANTS.RULES_CONFIG_SCHEMA_HASH);
285
+ assert(schemaHash === RELEASE_CONSTANTS.RULES_CONFIG_SCHEMA_HASH.toLowerCase(), "A Rules admission carries the Rules' schema id RULES_CONFIG_SCHEMA_HASH (keccak256 of RulesConfig's type string); " +
286
+ 'keccak256("hookr.rules.config") is the retired 15-word layout\'s');
287
+ const a = { kind: 0, ...p, schemaHash, gasLimit: RULES_GAS_LIMIT, phaseMask: 3, failOpen: false };
288
+ checkAdmission(a);
289
+ return a;
290
+ }
291
+ /**
292
+ * An advisory admission. `caps` is the advisory's admitted maximum; every pool that binds it reserves that maximum
293
+ * out of its caps (`poolCapsFor`).
294
+ */
295
+ export function advisoryAdmission(p) {
296
+ const a = { kind: 1, ...p };
297
+ checkAdmission(a);
298
+ return a;
299
+ }
300
+ /**
301
+ * A pair-advisory admission for a root factory: kind ADVISORY, surcharge cap `maxLpFeePips`, BEFORE_SWAP, fee-only,
302
+ * no quote take, fail closed unless `failOpen`. HookrRootFactory deploys a root naming the advisory only when the
303
+ * root's advisory cap and gas are at most these and the advisory's runtime codehash is `codeHash`; a fail-open root
304
+ * additionally needs a fail-open admission (the rule HookrRoot applies when it binds a fail-open advisory).
305
+ */
306
+ export function pairAdvisoryAdmission(p) {
307
+ assert(Number.isInteger(p.maxLpFeePips) &&
308
+ p.maxLpFeePips > 0 &&
309
+ p.maxLpFeePips <= RELEASE_CONSTANTS.PAIR_ROOT_MAX_LP_FEE_PIPS, "Pair advisory cap outside (0, HookrPairRoot.MAX_LP_FEE_PIPS]");
310
+ assert(Number.isInteger(p.gasLimit) && p.gasLimit <= RELEASE_CONSTANTS.PAIR_ROOT_MAX_ADVISORY_GAS, "Pair advisory gas above HookrPairRoot.MAX_ADVISORY_GAS");
311
+ const { maxLpFeePips, failOpen = false, ...rest } = p;
312
+ return advisoryAdmission({
313
+ ...rest,
314
+ caps: { maxLpFeePips, maxQuoteTakePips: 0, maxSubjectTakeBps: 0 },
315
+ phaseMask: PHASE.BEFORE_SWAP,
316
+ feeOnly: true,
317
+ failOpen,
318
+ });
319
+ }
320
+ /** The timelocked ADMIT of a pair advisory scoped to root factory `factory`. */
321
+ export function factoryAdmission(factory, admission) {
322
+ assert(admission.kind === ADMISSION_KIND.ADVISORY, "A root factory admits only advisories");
323
+ checkAdmission(admission);
324
+ return { kind: "ADMIT", root: address(factory), admission };
325
+ }
326
+ /**
327
+ * The checks HookrRootFactory.deploy runs on a pair root's advisory against its factory admission (read with
328
+ * `admission(factory, advisory)`) and the advisory's current runtime codehash. A fail-open root charges its full cap
329
+ * whenever the advisory fails, so it needs an admission that is fail-open, fee-only and without a quote take.
330
+ */
331
+ export function checkPairRootAdmission(params, admitted, advisoryCodeHash) {
332
+ const advisory = address(params.advisory);
333
+ assert(admitted.implementation.toLowerCase() === advisory && admitted.kind === ADMISSION_KIND.ADVISORY, "Advisory not admitted for the root factory");
334
+ assert(params.advisoryCapPips <= admitted.caps.maxLpFeePips, "Advisory cap above the factory admission");
335
+ assert(params.advisoryGasLimit <= admitted.gasLimit, "Advisory gas above the factory admission");
336
+ if (params.advisoryFailOpen)
337
+ assert(admitted.failOpen && admitted.feeOnly && admitted.caps.maxQuoteTakePips === 0, "Fail-open root needs a fail-open, fee-only factory admission without a quote take");
338
+ assert(hash(advisoryCodeHash) === admitted.codeHash.toLowerCase(), "Advisory runtime differs from its admission");
339
+ }
340
+ /**
341
+ * Brake (owner or guardian, immediate): permanently withdraws root factory `factory`'s admissions of the given pair
342
+ * advisories. Deployed pair roots keep their advisory; `rootStatus` then reads them as unverified.
343
+ */
344
+ export function buildRevokeFactoryAdmissions(m, factory, implementations) {
345
+ assert(implementations.length > 0, "Name at least one admission to revoke");
346
+ return buildCall(m, "registry", registryAbi, "revokeFactoryAdmissions", [
347
+ address(factory),
348
+ implementations.map((a) => address(a)),
349
+ ]);
350
+ }
351
+ /** `rootStatus(root)` calldata. */
352
+ export function encodeRootStatusCall(root) {
353
+ return encodeFunctionData({ abi: registryAbi, functionName: "rootStatus", args: [address(root)] });
354
+ }
355
+ /**
356
+ * Decodes a `rootStatus(root)` result. `verified` is true for a registered root whose advisory, if any, is still
357
+ * admitted for its factory with an unchanged runtime codehash; gate pair roots on it.
358
+ */
359
+ export function decodeRootStatus(data) {
360
+ const [advisory, verified] = decodeFunctionResult({
361
+ abi: registryAbi,
362
+ functionName: "rootStatus",
363
+ data,
364
+ });
365
+ return { advisory: advisory.toLowerCase(), verified };
366
+ }
367
+ /**
368
+ * Whether `code` is an EIP-7702 delegation designator (0xef0100 || target). The registry, the treasury and every
369
+ * HookrGoverned contract refuse such an account as owner, nominee or guardian.
370
+ */
371
+ export function isDelegatedCode(code) {
372
+ return code !== undefined && /^0xef0100[\da-fA-F]{40}$/.test(code);
373
+ }
374
+ /** Refuses an owner, nominee or guardian account whose code (from eth_getCode) is an EIP-7702 delegation. */
375
+ export function checkUndelegated(account, code) {
376
+ assert(!isDelegatedCode(code), "EIP-7702 delegated account");
377
+ return address(account, true);
378
+ }