maroo-viem-poc 0.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/dist/index.cjs ADDED
@@ -0,0 +1,740 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let viem = require("viem");
3
+ let viem_actions = require("viem/actions");
4
+ let viem_utils = require("viem/utils");
5
+ let _maroo_chain_contracts_abi_precompiles_pcl_IPcl = require("@maroo-chain/contracts/abi/precompiles/pcl/IPcl");
6
+ let _maroo_chain_contracts_abi_precompiles_agent_IAgent = require("@maroo-chain/contracts/abi/precompiles/agent/IAgent");
7
+ let _maroo_chain_contracts_abi_precompiles_eas_IEas = require("@maroo-chain/contracts/abi/precompiles/eas/IEas");
8
+ let _maroo_chain_contracts_abi_precompiles_okrw_IOkrw = require("@maroo-chain/contracts/abi/precompiles/okrw/IOkrw");
9
+ //#region src/constants.ts
10
+ /**
11
+ * Maroo static precompile addresses.
12
+ *
13
+ * Mirrors the `*_PRECOMPILED_ADDRESS` constants in
14
+ * `@maroo-chain/contracts/precompiles/{okrw,pcl,eas,agent}/I*.sol`.
15
+ *
16
+ * The contracts package does not export these from its TypeScript build
17
+ * (only the `.sol` sources carry them), so we redeclare them here.
18
+ * `test/addresses.test.ts` asserts these still match the installed package.
19
+ */
20
+ const precompileAddresses = {
21
+ okrw: "0x1000000000000000000000000000000000000001",
22
+ pcl: "0x1000000000000000000000000000000000000005",
23
+ /** Returns `eas` module params. Not the EAS contract itself — see {@link easContracts}. */
24
+ eas: "0x1000000000000000000000000000000000000009",
25
+ agent: "0x100000000000000000000000000000000000000A",
26
+ /** Clairveil shielded pool. Not yet deployed on marooTestnet. */
27
+ privacy: "0x100000000000000000000000000000000000000b"
28
+ };
29
+ /**
30
+ * EAS contracts, as reported by `eas.getParams()` on marooTestnet.
31
+ *
32
+ * These sit at precompile-shaped addresses but implement the standard
33
+ * Ethereum Attestation Service interfaces. `@maroo-chain/contracts@0.0.6`
34
+ * ships no ABI for them; use `@ethereum-attestation-service/eas-contracts`.
35
+ *
36
+ * Do not hardcode these in application code — read them from
37
+ * `client.eas.getParams()`, which is authoritative per network.
38
+ */
39
+ const easContracts = {
40
+ schemaRegistry: "0x1000000000000000000000000000000000000006",
41
+ eas: "0x1000000000000000000000000000000000000007",
42
+ indexer: "0x1000000000000000000000000000000000000008"
43
+ };
44
+ /**
45
+ * ABI entries that exist for chain-side codegen or proxy plumbing and must
46
+ * never be surfaced as client actions.
47
+ *
48
+ * - `_policies` is a `pure` no-op declared solely so the Solidity compiler
49
+ * emits the policy structs into the ABI (see the comment at the bottom of
50
+ * `IPcl.sol`). Calling it does nothing.
51
+ * - `preCall`/`postCall` are invoked by the PCL proxy hook with a `principal`
52
+ * the caller cannot forge. Application code goes through {@link runOnPcl}.
53
+ */
54
+ const excludedFunctions = { pcl: [
55
+ "_policies",
56
+ "preCall",
57
+ "postCall"
58
+ ] };
59
+ /** The selector wildcard. An empty `selector` matches every function. */
60
+ const SELECTOR_ALL = "0x";
61
+ /** `VolumeUnitPolicy.maxLimit` sentinel meaning "no upper bound". */
62
+ const NO_MAX_LIMIT = viem.maxUint256;
63
+ //#endregion
64
+ //#region src/core/precompileActions.ts
65
+ const isRead = (m) => m === "view" || m === "pure";
66
+ /**
67
+ * Build a typed action map from an `as const` ABI by walking it at runtime.
68
+ *
69
+ * Replaces per-precompile codegen: every function in `abi` becomes one method,
70
+ * dispatched to `readContract` or `writeContract` by its state mutability.
71
+ *
72
+ * Overloads that agree on mutability share one dispatcher (viem resolves the
73
+ * overload from `args`). Overloads that mix read and write mutability throw at
74
+ * factory construction — a single dispatcher would silently misroute one side.
75
+ *
76
+ * @param abi - The precompile interface, as an `as const` ABI.
77
+ * @param defaultAddress - Where the precompile lives on a stock Maroo chain.
78
+ * @param options.mode - Emit only reads, only writes, or both.
79
+ * @param options.exclude - Function names to omit (codegen dummies, proxy hooks).
80
+ */
81
+ function precompileActions(abi, defaultAddress, options = {}) {
82
+ const { mode = "all", exclude = [] } = options;
83
+ const excluded = new Set(exclude);
84
+ const classification = /* @__PURE__ */ new Map();
85
+ const payableNames = /* @__PURE__ */ new Set();
86
+ for (const item of abi) {
87
+ if (item.type !== "function" || excluded.has(item.name)) continue;
88
+ const read = isRead(item.stateMutability);
89
+ const prev = classification.get(item.name);
90
+ if (prev !== void 0 && prev !== read) throw new Error(`precompileActions: "${item.name}" has overloads with mixed read/write mutability; add it to options.exclude and wrap it by hand`);
91
+ classification.set(item.name, read);
92
+ if (item.stateMutability === "payable") payableNames.add(item.name);
93
+ }
94
+ return (config = {}) => (client) => {
95
+ const actions = {};
96
+ for (const item of abi) {
97
+ if (item.type !== "function") continue;
98
+ if (excluded.has(item.name)) continue;
99
+ if (actions[item.name]) continue;
100
+ const read = isRead(item.stateMutability);
101
+ if (mode === "read" && !read) continue;
102
+ if (mode === "write" && read) continue;
103
+ const functionName = item.name;
104
+ const payable = payableNames.has(functionName);
105
+ const resolveCall = (parameters) => {
106
+ const { address, args, value: _value, ...rest } = parameters;
107
+ return {
108
+ ...rest,
109
+ abi,
110
+ functionName,
111
+ args: args ?? [],
112
+ address: address ?? config.address ?? defaultAddress
113
+ };
114
+ };
115
+ const fn = (parameters = {}) => {
116
+ const call = resolveCall(parameters);
117
+ if (read) return (0, viem_utils.getAction)(client, viem_actions.readContract, "readContract")(call);
118
+ const value = payable ? parameters.value : void 0;
119
+ return (0, viem_utils.getAction)(client, viem_actions.writeContract, "writeContract")({
120
+ ...call,
121
+ ...value !== void 0 ? { value } : {}
122
+ });
123
+ };
124
+ if (read) fn.call = (parameters = {}) => resolveCall(parameters);
125
+ actions[functionName] = fn;
126
+ }
127
+ return actions;
128
+ };
129
+ }
130
+ //#endregion
131
+ //#region src/actions/runOnPcl.ts
132
+ /**
133
+ * Call a contract *through* the PCL precompile so that contract-scoped
134
+ * policies are enforced.
135
+ *
136
+ * A direct call to the same contract enforces **global policies only**. There
137
+ * is nothing in the target contract's ABI, nor in any revert, that signals
138
+ * this — the call simply succeeds with contract policies unapplied. Route
139
+ * through `runOnPcl` whenever the target has policies registered.
140
+ *
141
+ * For a contract call the parameter shape mirrors `writeContract`, so
142
+ * switching a call site is a one-word change. Omit `abi`/`functionName` to
143
+ * forward a bare native transfer, or pass pre-encoded `data`.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const hash = await client.pcl.runOnPcl({
148
+ * address: token,
149
+ * abi: erc20Abi,
150
+ * functionName: 'transfer',
151
+ * args: [recipient, amount],
152
+ * })
153
+ *
154
+ * // Native transfer, policy-checked:
155
+ * const hash = await client.pcl.runOnPcl({ address: recipient, value: parseEther('1') })
156
+ * ```
157
+ */
158
+ async function runOnPcl(client, parameters) {
159
+ const { address, value = 0n, gas, pclAddress } = parameters;
160
+ const data = parameters.abi ? (0, viem.encodeFunctionData)({
161
+ abi: parameters.abi,
162
+ functionName: parameters.functionName,
163
+ args: parameters.args
164
+ }) : parameters.data ?? "0x";
165
+ return (0, viem_utils.getAction)(client, viem_actions.writeContract, "writeContract")({
166
+ address: pclAddress ?? precompileAddresses.pcl,
167
+ abi: _maroo_chain_contracts_abi_precompiles_pcl_IPcl.iPclAbi,
168
+ functionName: "runOnPcl",
169
+ args: [
170
+ address,
171
+ data,
172
+ value
173
+ ],
174
+ gas
175
+ });
176
+ }
177
+ //#endregion
178
+ //#region src/decorators/precompiles.ts
179
+ const pclReadActions = precompileActions(_maroo_chain_contracts_abi_precompiles_pcl_IPcl.iPclAbi, precompileAddresses.pcl, {
180
+ mode: "read",
181
+ exclude: excludedFunctions.pcl
182
+ });
183
+ const okrwReadActions = precompileActions(_maroo_chain_contracts_abi_precompiles_okrw_IOkrw.iOkrwAbi, precompileAddresses.okrw, { mode: "read" });
184
+ const easReadActions = precompileActions(_maroo_chain_contracts_abi_precompiles_eas_IEas.iEasAbi, precompileAddresses.eas, { mode: "read" });
185
+ const agentReadActions = precompileActions(_maroo_chain_contracts_abi_precompiles_agent_IAgent.iAgentAbi, precompileAddresses.agent, { mode: "read" });
186
+ const pclWriteActions = precompileActions(_maroo_chain_contracts_abi_precompiles_pcl_IPcl.iPclAbi, precompileAddresses.pcl, {
187
+ mode: "write",
188
+ exclude: excludedFunctions.pcl
189
+ });
190
+ const okrwWriteActions = precompileActions(_maroo_chain_contracts_abi_precompiles_okrw_IOkrw.iOkrwAbi, precompileAddresses.okrw, { mode: "write" });
191
+ //#endregion
192
+ //#region src/decorators/maroo.ts
193
+ /**
194
+ * Read actions for every Maroo precompile.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * const client = createPublicClient({ chain: marooTestnet, transport: http() })
199
+ * .extend(marooPublicActions())
200
+ *
201
+ * const { policyAdmin, entrypoints } = await client.pcl.getParams()
202
+ * const { policies } = await client.pcl.globalPolicies()
203
+ * const [ids, page] = await client.agent.getAgentIds({ args: [wallet, pageRequest] })
204
+ * ```
205
+ */
206
+ function marooPublicActions() {
207
+ return (client) => ({
208
+ pcl: pclReadActions()(client),
209
+ okrw: okrwReadActions()(client),
210
+ eas: easReadActions()(client),
211
+ agent: agentReadActions()(client)
212
+ });
213
+ }
214
+ /**
215
+ * @example
216
+ * ```ts
217
+ * const client = createWalletClient({ chain: marooTestnet, account, transport: http() })
218
+ * .extend(marooWalletActions())
219
+ *
220
+ * const hash = await client.pcl.runOnPcl({
221
+ * address: token, abi: erc20Abi, functionName: 'transfer', args: [to, amount],
222
+ * })
223
+ * ```
224
+ */
225
+ function marooWalletActions() {
226
+ return (client) => ({
227
+ pcl: {
228
+ ...pclWriteActions()(client),
229
+ runOnPcl: (parameters) => runOnPcl(client, parameters)
230
+ },
231
+ okrw: okrwWriteActions()(client)
232
+ });
233
+ }
234
+ //#endregion
235
+ //#region src/codec/policy.ts
236
+ /**
237
+ * PCL policy codec.
238
+ *
239
+ * `PolicySet.policy` is an opaque `bytes` field whose layout is chosen by the
240
+ * sibling `templateId` string. That mapping exists nowhere in the ABI — the
241
+ * `_policies` dummy function only forces the structs into the artifact. This
242
+ * module is the mapping.
243
+ *
244
+ * Verified against `marooTestnet.globalPolicies()` at block 12447862.
245
+ */
246
+ const policyTemplateIds = [
247
+ "EAS_POLICY",
248
+ "DENYLIST_POLICY",
249
+ "VOLUME_POLICY",
250
+ "OKRW_EAS_TRANSFER_LIMIT_POLICY",
251
+ "PERIODIC_VOLUME_POLICY",
252
+ "AGENT_OKRW_TRANSFER_LIMIT_POLICY",
253
+ "OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY",
254
+ "LOGICAL_POLICY",
255
+ "FOR_EACH_POLICY"
256
+ ];
257
+ const LogicalQuantifier = {
258
+ Unspecified: 0,
259
+ And: 1,
260
+ Or: 2
261
+ };
262
+ const ForEachQuantifier = {
263
+ Unspecified: 0,
264
+ Any: 1,
265
+ Every: 2
266
+ };
267
+ const ForEachSubject = {
268
+ Unspecified: 0,
269
+ AgentOwners: 1
270
+ };
271
+ const nameOf = (e, v) => Object.keys(e).find((k) => e[k] === v) ?? "Unspecified";
272
+ const POLICY_SET_COMPONENTS = [
273
+ {
274
+ name: "templateId",
275
+ type: "string"
276
+ },
277
+ {
278
+ name: "policy",
279
+ type: "bytes"
280
+ },
281
+ {
282
+ name: "selector",
283
+ type: "bytes"
284
+ }
285
+ ];
286
+ /**
287
+ * `templateId` → the ABI parameters of its `policy` bytes payload.
288
+ *
289
+ * Each entry is a one-element parameter list wrapping the payload tuple, so
290
+ * `decodeAbiParameters(codec, bytes)[0]` is the decoded struct.
291
+ */
292
+ const policyCodecs = {
293
+ EAS_POLICY: [{
294
+ name: "policy",
295
+ type: "tuple",
296
+ components: [
297
+ {
298
+ name: "easContract",
299
+ type: "address"
300
+ },
301
+ {
302
+ name: "indexContract",
303
+ type: "address"
304
+ },
305
+ {
306
+ name: "schemaUid",
307
+ type: "bytes32"
308
+ }
309
+ ]
310
+ }],
311
+ DENYLIST_POLICY: [{
312
+ name: "policy",
313
+ type: "tuple",
314
+ components: [{
315
+ name: "addresses",
316
+ type: "address[]"
317
+ }]
318
+ }],
319
+ VOLUME_POLICY: [{
320
+ name: "policy",
321
+ type: "tuple",
322
+ components: [{
323
+ name: "tokens",
324
+ type: "string[]"
325
+ }, {
326
+ name: "limits",
327
+ type: "tuple[]",
328
+ components: [{
329
+ name: "minLimit",
330
+ type: "uint256"
331
+ }, {
332
+ name: "maxLimit",
333
+ type: "uint256"
334
+ }]
335
+ }]
336
+ }],
337
+ PERIODIC_VOLUME_POLICY: [{
338
+ name: "policy",
339
+ type: "tuple",
340
+ components: [{
341
+ name: "tokens",
342
+ type: "string[]"
343
+ }, {
344
+ name: "limits",
345
+ type: "tuple[]",
346
+ components: [{
347
+ name: "maxAmount",
348
+ type: "uint256"
349
+ }, {
350
+ name: "resetPeriodSeconds",
351
+ type: "uint64"
352
+ }]
353
+ }]
354
+ }],
355
+ OKRW_EAS_TRANSFER_LIMIT_POLICY: [{
356
+ name: "policy",
357
+ type: "tuple",
358
+ components: [
359
+ {
360
+ name: "easContract",
361
+ type: "address"
362
+ },
363
+ {
364
+ name: "indexContract",
365
+ type: "address"
366
+ },
367
+ {
368
+ name: "schemaUid",
369
+ type: "bytes32"
370
+ },
371
+ {
372
+ name: "transferLimitAmount",
373
+ type: "uint256"
374
+ }
375
+ ]
376
+ }],
377
+ OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY: [{
378
+ name: "policy",
379
+ type: "tuple",
380
+ components: [
381
+ {
382
+ name: "easContract",
383
+ type: "address"
384
+ },
385
+ {
386
+ name: "indexContract",
387
+ type: "address"
388
+ },
389
+ {
390
+ name: "schemaUid",
391
+ type: "bytes32"
392
+ },
393
+ {
394
+ name: "maxAmount",
395
+ type: "uint256"
396
+ },
397
+ {
398
+ name: "resetPeriodSeconds",
399
+ type: "uint64"
400
+ }
401
+ ]
402
+ }],
403
+ AGENT_OKRW_TRANSFER_LIMIT_POLICY: [{
404
+ name: "policy",
405
+ type: "tuple",
406
+ components: [{
407
+ name: "reserved",
408
+ type: "uint256"
409
+ }]
410
+ }],
411
+ LOGICAL_POLICY: [{
412
+ name: "policy",
413
+ type: "tuple",
414
+ components: [{
415
+ name: "quantifier",
416
+ type: "uint8"
417
+ }, {
418
+ name: "children",
419
+ type: "tuple[]",
420
+ components: POLICY_SET_COMPONENTS
421
+ }]
422
+ }],
423
+ FOR_EACH_POLICY: [{
424
+ name: "policy",
425
+ type: "tuple",
426
+ components: [
427
+ {
428
+ name: "quantifier",
429
+ type: "uint8"
430
+ },
431
+ {
432
+ name: "subject",
433
+ type: "uint8"
434
+ },
435
+ {
436
+ name: "child",
437
+ type: "tuple",
438
+ components: POLICY_SET_COMPONENTS
439
+ }
440
+ ]
441
+ }]
442
+ };
443
+ const MAX_DEPTH = 8;
444
+ const isKnown = (id) => Object.prototype.hasOwnProperty.call(policyCodecs, id);
445
+ /**
446
+ * Recursively decode a `PolicySet` returned by `globalPolicies()` or
447
+ * `contractPolicies()` into a readable tree.
448
+ *
449
+ * Unrecognised template ids decode to `{ unknown: true, raw }` rather than
450
+ * throwing, so a chain upgrade that adds a policy type degrades a dApp's
451
+ * policy inspector instead of breaking it.
452
+ */
453
+ function decodePolicySet(set, depth = 0) {
454
+ if (depth > MAX_DEPTH) throw new Error(`policy nesting exceeded depth ${MAX_DEPTH}`);
455
+ const { selector } = set;
456
+ if (!isKnown(set.templateId)) return {
457
+ templateId: set.templateId,
458
+ selector,
459
+ raw: set.policy,
460
+ unknown: true
461
+ };
462
+ switch (set.templateId) {
463
+ case "EAS_POLICY": {
464
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.EAS_POLICY, set.policy);
465
+ return {
466
+ templateId: "EAS_POLICY",
467
+ selector,
468
+ easContract: p.easContract,
469
+ indexContract: p.indexContract,
470
+ schemaUid: p.schemaUid
471
+ };
472
+ }
473
+ case "DENYLIST_POLICY": {
474
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.DENYLIST_POLICY, set.policy);
475
+ return {
476
+ templateId: "DENYLIST_POLICY",
477
+ selector,
478
+ addresses: p.addresses
479
+ };
480
+ }
481
+ case "VOLUME_POLICY": {
482
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.VOLUME_POLICY, set.policy);
483
+ if (p.tokens.length !== p.limits.length) return {
484
+ templateId: set.templateId,
485
+ selector,
486
+ raw: set.policy,
487
+ unknown: true
488
+ };
489
+ return {
490
+ templateId: "VOLUME_POLICY",
491
+ selector,
492
+ limits: p.tokens.map((token, i) => ({
493
+ token,
494
+ minLimit: p.limits[i].minLimit,
495
+ maxLimit: p.limits[i].maxLimit
496
+ }))
497
+ };
498
+ }
499
+ case "PERIODIC_VOLUME_POLICY": {
500
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.PERIODIC_VOLUME_POLICY, set.policy);
501
+ if (p.tokens.length !== p.limits.length) return {
502
+ templateId: set.templateId,
503
+ selector,
504
+ raw: set.policy,
505
+ unknown: true
506
+ };
507
+ return {
508
+ templateId: "PERIODIC_VOLUME_POLICY",
509
+ selector,
510
+ limits: p.tokens.map((token, i) => ({
511
+ token,
512
+ maxAmount: p.limits[i].maxAmount,
513
+ resetPeriodSeconds: p.limits[i].resetPeriodSeconds
514
+ }))
515
+ };
516
+ }
517
+ case "OKRW_EAS_TRANSFER_LIMIT_POLICY": {
518
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.OKRW_EAS_TRANSFER_LIMIT_POLICY, set.policy);
519
+ return {
520
+ templateId: "OKRW_EAS_TRANSFER_LIMIT_POLICY",
521
+ selector,
522
+ easContract: p.easContract,
523
+ indexContract: p.indexContract,
524
+ schemaUid: p.schemaUid,
525
+ transferLimitAmount: p.transferLimitAmount
526
+ };
527
+ }
528
+ case "OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY": {
529
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY, set.policy);
530
+ return {
531
+ templateId: "OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY",
532
+ selector,
533
+ easContract: p.easContract,
534
+ indexContract: p.indexContract,
535
+ schemaUid: p.schemaUid,
536
+ maxAmount: p.maxAmount,
537
+ resetPeriodSeconds: p.resetPeriodSeconds
538
+ };
539
+ }
540
+ case "AGENT_OKRW_TRANSFER_LIMIT_POLICY":
541
+ (0, viem.decodeAbiParameters)(policyCodecs.AGENT_OKRW_TRANSFER_LIMIT_POLICY, set.policy);
542
+ return {
543
+ templateId: "AGENT_OKRW_TRANSFER_LIMIT_POLICY",
544
+ selector
545
+ };
546
+ case "LOGICAL_POLICY": {
547
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.LOGICAL_POLICY, set.policy);
548
+ return {
549
+ templateId: "LOGICAL_POLICY",
550
+ selector,
551
+ quantifier: nameOf(LogicalQuantifier, p.quantifier),
552
+ children: p.children.map((c) => decodePolicySet(c, depth + 1))
553
+ };
554
+ }
555
+ case "FOR_EACH_POLICY": {
556
+ const [p] = (0, viem.decodeAbiParameters)(policyCodecs.FOR_EACH_POLICY, set.policy);
557
+ return {
558
+ templateId: "FOR_EACH_POLICY",
559
+ selector,
560
+ quantifier: nameOf(ForEachQuantifier, p.quantifier),
561
+ subject: nameOf(ForEachSubject, p.subject),
562
+ child: decodePolicySet(p.child, depth + 1)
563
+ };
564
+ }
565
+ default: return {
566
+ templateId: set.templateId,
567
+ selector,
568
+ raw: set.policy,
569
+ unknown: true
570
+ };
571
+ }
572
+ }
573
+ /**
574
+ * ABI-encode a policy payload for its template.
575
+ *
576
+ * The `policy` argument is typed per `templateId` via {@link PolicyInput}, so
577
+ * the public signature is fully checked. The single internal cast is the
578
+ * unavoidable one: indexing `policyCodecs` by a generic `id` yields a union
579
+ * that `encodeAbiParameters` cannot unify positionally (viem does the same at
580
+ * this boundary).
581
+ *
582
+ * Admin-facing: only `policyAdmin` may set global policies, and only a
583
+ * contract's registered admin may change its contract policies.
584
+ */
585
+ function encodePolicy(templateId, policy) {
586
+ const codec = policyCodecs[templateId];
587
+ return (0, viem.encodeAbiParameters)(codec, [policy]);
588
+ }
589
+ /**
590
+ * Normalize a function selector for a `PolicySet`.
591
+ *
592
+ * The chain accepts either an empty value (wildcard, stored internally as
593
+ * `"__all__"`) or exactly 4 bytes, lowercased. Anything else is rejected with
594
+ * `InvalidSelector`.
595
+ */
596
+ function normalizeSelector(selector) {
597
+ if (!selector || selector === "0x") return "0x";
598
+ const body = selector.slice(2).toLowerCase();
599
+ if (!/^[0-9a-f]{8}$/.test(body)) throw new Error(`selector must be 4 bytes of hex (8 hex chars), got "${selector}"`);
600
+ return `0x${body}`;
601
+ }
602
+ //#endregion
603
+ //#region src/errors/pcl.ts
604
+ /**
605
+ * The errors PCL actually declares. Used to refuse errors we do not own —
606
+ * `decodeErrorResult` also recognises the built-in `Error(string)` / `Panic`
607
+ * signatures, and those must decode to `null`, not to a policy verdict.
608
+ */
609
+ const PCL_ERROR_NAMES = new Set(_maroo_chain_contracts_abi_precompiles_pcl_IPcl.iPclAbi.flatMap((item) => item.type === "error" ? [item.name] : []));
610
+ /**
611
+ * `AnyOfRejected` nests (a LOGICAL(Or) branch can itself be a LOGICAL(Or)),
612
+ * and revert bytes are attacker-controlled via `eth_call` — cap the recursion
613
+ * like the policy codec caps its tree depth. Past the cap, children surface
614
+ * as `undecodable` instead of overflowing the stack inside an error handler.
615
+ */
616
+ const MAX_ERROR_DEPTH = 8;
617
+ const ATTESTATION = {
618
+ EasAttestationRequired: "required",
619
+ EasNoAttestationReceived: "none-received",
620
+ EasAttestationLookupFailed: "lookup-failed",
621
+ EasAttestationRevoked: "revoked",
622
+ EasAttestationExpired: "expired"
623
+ };
624
+ function toViolation(name, args, depth) {
625
+ const a = args;
626
+ switch (name) {
627
+ case "InDenylist": return {
628
+ kind: "denylist",
629
+ sender: a[0]
630
+ };
631
+ case "VolumeBelowMinLimit": return {
632
+ kind: "volume-below-min",
633
+ minLimit: a[0],
634
+ value: a[1]
635
+ };
636
+ case "VolumeAboveMaxLimit": return {
637
+ kind: "volume-above-max",
638
+ maxLimit: a[0],
639
+ value: a[1]
640
+ };
641
+ case "ExceededPeriodicVolume": return {
642
+ kind: "periodic-volume",
643
+ maxLimit: a[0],
644
+ value: a[1],
645
+ resetAt: /* @__PURE__ */ new Date(Number(a[2]) * 1e3)
646
+ };
647
+ case "ReachedLimitOfNonEAS": return {
648
+ kind: "non-eas-limit",
649
+ maxLimit: a[0],
650
+ value: a[1]
651
+ };
652
+ case "ExceededAgentTransferLimit": return {
653
+ kind: "agent-transfer-limit",
654
+ maxLimit: a[0],
655
+ value: a[1]
656
+ };
657
+ case "Unauthorized": return { kind: "unauthorized" };
658
+ case "AnyOfRejected": return {
659
+ kind: "any-of-rejected",
660
+ children: (a[0] ?? []).map((child) => decode(child, depth + 1) ?? {
661
+ kind: "unknown",
662
+ name: "undecodable"
663
+ })
664
+ };
665
+ default:
666
+ if (name in ATTESTATION) return {
667
+ kind: "attestation",
668
+ reason: ATTESTATION[name],
669
+ sender: a[0]
670
+ };
671
+ return {
672
+ kind: "unknown",
673
+ name,
674
+ args
675
+ };
676
+ }
677
+ }
678
+ function decode(source, depth) {
679
+ if (depth > MAX_ERROR_DEPTH) return null;
680
+ let data;
681
+ if (typeof source === "string" && source.startsWith("0x")) data = source;
682
+ else if (source instanceof viem.BaseError) {
683
+ const revert = source.walk((e) => e instanceof viem.ContractFunctionRevertedError);
684
+ if (revert instanceof viem.ContractFunctionRevertedError) data = revert.raw;
685
+ }
686
+ if (!data || data === "0x") return null;
687
+ try {
688
+ const { errorName, args } = (0, viem.decodeErrorResult)({
689
+ abi: _maroo_chain_contracts_abi_precompiles_pcl_IPcl.iPclAbi,
690
+ data
691
+ });
692
+ if (!PCL_ERROR_NAMES.has(errorName)) return null;
693
+ return toViolation(errorName, args ?? [], depth);
694
+ } catch {
695
+ return null;
696
+ }
697
+ }
698
+ /**
699
+ * Decode PCL revert data — or a thrown viem error wrapping it — into a
700
+ * {@link PclViolation}. Returns `null` if the revert is not a PCL error.
701
+ *
702
+ * @example
703
+ * ```ts
704
+ * try {
705
+ * await client.pcl.runOnPcl({ ... })
706
+ * } catch (err) {
707
+ * const v = decodePclError(err)
708
+ * if (v?.kind === 'periodic-volume')
709
+ * throw new Error(`24h limit hit. Resets ${v.resetAt.toISOString()}`)
710
+ * throw err
711
+ * }
712
+ * ```
713
+ */
714
+ function decodePclError(source) {
715
+ return decode(source, 0);
716
+ }
717
+ //#endregion
718
+ exports.ForEachQuantifier = ForEachQuantifier;
719
+ exports.ForEachSubject = ForEachSubject;
720
+ exports.LogicalQuantifier = LogicalQuantifier;
721
+ exports.NO_MAX_LIMIT = NO_MAX_LIMIT;
722
+ exports.SELECTOR_ALL = SELECTOR_ALL;
723
+ exports.agentReadActions = agentReadActions;
724
+ exports.decodePclError = decodePclError;
725
+ exports.decodePolicySet = decodePolicySet;
726
+ exports.easContracts = easContracts;
727
+ exports.easReadActions = easReadActions;
728
+ exports.encodePolicy = encodePolicy;
729
+ exports.marooPublicActions = marooPublicActions;
730
+ exports.marooWalletActions = marooWalletActions;
731
+ exports.normalizeSelector = normalizeSelector;
732
+ exports.okrwReadActions = okrwReadActions;
733
+ exports.okrwWriteActions = okrwWriteActions;
734
+ exports.pclReadActions = pclReadActions;
735
+ exports.pclWriteActions = pclWriteActions;
736
+ exports.policyCodecs = policyCodecs;
737
+ exports.policyTemplateIds = policyTemplateIds;
738
+ exports.precompileActions = precompileActions;
739
+ exports.precompileAddresses = precompileAddresses;
740
+ exports.runOnPcl = runOnPcl;