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/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "maroo-viem-poc",
3
+ "version": "0.1.0",
4
+ "description": "PoC viem extension for the Maroo chain — namespaced precompile actions (pcl/okrw/eas/agent), PCL policy codec, runOnPcl wrapper, and typed policy-violation decoding. Verified against live marooTestnet.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "license": "MIT",
8
+ "engines": {
9
+ "node": ">=20"
10
+ },
11
+ "keywords": [
12
+ "maroo",
13
+ "viem",
14
+ "precompile",
15
+ "pcl",
16
+ "cosmos-evm",
17
+ "poc"
18
+ ],
19
+ "files": [
20
+ "dist",
21
+ "src",
22
+ "README.md"
23
+ ],
24
+ "exports": {
25
+ ".": {
26
+ "import": {
27
+ "types": "./dist/index.d.mts",
28
+ "default": "./dist/index.mjs"
29
+ },
30
+ "require": {
31
+ "types": "./dist/index.d.cts",
32
+ "default": "./dist/index.cjs"
33
+ }
34
+ }
35
+ },
36
+ "main": "./dist/index.cjs",
37
+ "module": "./dist/index.mjs",
38
+ "types": "./dist/index.d.cts",
39
+ "dependencies": {
40
+ "@maroo-chain/contracts": "0.0.6",
41
+ "abitype": "^1.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "viem": "^2.44.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^26.1.1",
48
+ "tsdown": "^0.21.0",
49
+ "typescript": "^5.7.0",
50
+ "viem": "^2.44.0",
51
+ "vitest": "^2.1.9"
52
+ },
53
+ "scripts": {
54
+ "build": "tsdown",
55
+ "typecheck": "tsc --noEmit",
56
+ "test": "vitest run test/unit",
57
+ "test:live": "vitest run test/live"
58
+ }
59
+ }
@@ -0,0 +1,117 @@
1
+ import { iPclAbi } from '@maroo-chain/contracts/abi/precompiles/pcl/IPcl'
2
+ import {
3
+ type Abi,
4
+ type Account,
5
+ type Address,
6
+ type Chain,
7
+ type Client,
8
+ type ContractFunctionArgs,
9
+ type ContractFunctionName,
10
+ type Hex,
11
+ type Transport,
12
+ type WriteContractReturnType,
13
+ encodeFunctionData,
14
+ } from 'viem'
15
+ import { writeContract } from 'viem/actions'
16
+ import { getAction } from 'viem/utils'
17
+ import { precompileAddresses } from '../constants.js'
18
+
19
+ type RunOnPclBase = {
20
+ /** The contract — or plain account — to call *through* PCL. */
21
+ address: Address
22
+ /** Native value forwarded to `address`. Debited from the caller, not from `msg.value`. */
23
+ value?: bigint | undefined
24
+ gas?: bigint | undefined
25
+ /** Override the PCL precompile address. */
26
+ pclAddress?: Address | undefined
27
+ }
28
+
29
+ type RunOnPclContractCall<
30
+ abi extends Abi | readonly unknown[],
31
+ functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'>,
32
+ > = RunOnPclBase & {
33
+ abi: abi
34
+ functionName: functionName
35
+ args?: ContractFunctionArgs<abi, 'nonpayable' | 'payable', functionName>
36
+ data?: undefined
37
+ }
38
+
39
+ /** A bare value transfer, or a call whose calldata you already encoded. */
40
+ type RunOnPclRawCall = RunOnPclBase & {
41
+ abi?: undefined
42
+ functionName?: undefined
43
+ args?: undefined
44
+ data?: Hex | undefined
45
+ }
46
+
47
+ export type RunOnPclParameters<
48
+ abi extends Abi | readonly unknown[] = Abi,
49
+ functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'> = ContractFunctionName<
50
+ abi,
51
+ 'nonpayable' | 'payable'
52
+ >,
53
+ > = RunOnPclContractCall<abi, functionName> | RunOnPclRawCall
54
+
55
+ export type RunOnPclReturnType = WriteContractReturnType
56
+
57
+ /**
58
+ * Call a contract *through* the PCL precompile so that contract-scoped
59
+ * policies are enforced.
60
+ *
61
+ * A direct call to the same contract enforces **global policies only**. There
62
+ * is nothing in the target contract's ABI, nor in any revert, that signals
63
+ * this — the call simply succeeds with contract policies unapplied. Route
64
+ * through `runOnPcl` whenever the target has policies registered.
65
+ *
66
+ * For a contract call the parameter shape mirrors `writeContract`, so
67
+ * switching a call site is a one-word change. Omit `abi`/`functionName` to
68
+ * forward a bare native transfer, or pass pre-encoded `data`.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const hash = await client.pcl.runOnPcl({
73
+ * address: token,
74
+ * abi: erc20Abi,
75
+ * functionName: 'transfer',
76
+ * args: [recipient, amount],
77
+ * })
78
+ *
79
+ * // Native transfer, policy-checked:
80
+ * const hash = await client.pcl.runOnPcl({ address: recipient, value: parseEther('1') })
81
+ * ```
82
+ */
83
+ export async function runOnPcl<
84
+ chain extends Chain | undefined,
85
+ account extends Account | undefined,
86
+ const abi extends Abi | readonly unknown[],
87
+ functionName extends ContractFunctionName<abi, 'nonpayable' | 'payable'>,
88
+ >(
89
+ client: Client<Transport, chain, account>,
90
+ parameters: RunOnPclParameters<abi, functionName>,
91
+ ): Promise<RunOnPclReturnType> {
92
+ const { address, value = 0n, gas, pclAddress } = parameters
93
+
94
+ const data: Hex = parameters.abi
95
+ ? (encodeFunctionData({
96
+ abi: parameters.abi,
97
+ functionName: parameters.functionName,
98
+ args: parameters.args,
99
+ } as never) as Hex)
100
+ : (parameters.data ?? '0x')
101
+
102
+ return getAction(
103
+ client,
104
+ writeContract,
105
+ 'writeContract',
106
+ )({
107
+ address: pclAddress ?? precompileAddresses.pcl,
108
+ abi: iPclAbi,
109
+ functionName: 'runOnPcl',
110
+ args: [address, data, value],
111
+ // `value` rides on the argument, never on the outer tx. The precompile
112
+ // issues `evm.Call(caller, target, data, gas, value)`, which debits the
113
+ // original caller directly. Attaching `msg.value` here would strand funds
114
+ // at the precompile.
115
+ gas,
116
+ } as never)
117
+ }
@@ -0,0 +1,381 @@
1
+ import type { AbiParametersToPrimitiveTypes } from 'abitype'
2
+ import { type Hex, decodeAbiParameters, encodeAbiParameters } from 'viem'
3
+
4
+ /**
5
+ * PCL policy codec.
6
+ *
7
+ * `PolicySet.policy` is an opaque `bytes` field whose layout is chosen by the
8
+ * sibling `templateId` string. That mapping exists nowhere in the ABI — the
9
+ * `_policies` dummy function only forces the structs into the artifact. This
10
+ * module is the mapping.
11
+ *
12
+ * Verified against `marooTestnet.globalPolicies()` at block 12447862.
13
+ */
14
+
15
+ // ─── Template ids ────────────────────────────────────────────────────────────
16
+ // Confirmed live: every id below returns a registered template from
17
+ // `pcl.policyTemplate(id)` on marooTestnet. Note FOR_EACH_POLICY, not
18
+ // FOREACH_POLICY — the underscore is load-bearing.
19
+
20
+ export const policyTemplateIds = [
21
+ 'EAS_POLICY',
22
+ 'DENYLIST_POLICY',
23
+ 'VOLUME_POLICY',
24
+ 'OKRW_EAS_TRANSFER_LIMIT_POLICY',
25
+ 'PERIODIC_VOLUME_POLICY',
26
+ 'AGENT_OKRW_TRANSFER_LIMIT_POLICY',
27
+ 'OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY',
28
+ 'LOGICAL_POLICY',
29
+ 'FOR_EACH_POLICY',
30
+ ] as const
31
+
32
+ export type PolicyTemplateId = (typeof policyTemplateIds)[number]
33
+
34
+ // ─── Enums (IPcl.sol) ────────────────────────────────────────────────────────
35
+
36
+ export const LogicalQuantifier = { Unspecified: 0, And: 1, Or: 2 } as const
37
+ export const ForEachQuantifier = { Unspecified: 0, Any: 1, Every: 2 } as const
38
+ export const ForEachSubject = { Unspecified: 0, AgentOwners: 1 } as const
39
+
40
+ export type LogicalQuantifierName = keyof typeof LogicalQuantifier
41
+ export type ForEachQuantifierName = keyof typeof ForEachQuantifier
42
+ export type ForEachSubjectName = keyof typeof ForEachSubject
43
+
44
+ const nameOf = <T extends Record<string, number>>(e: T, v: number): keyof T =>
45
+ (Object.keys(e) as (keyof T)[]).find((k) => e[k] === v) ?? ('Unspecified' as keyof T)
46
+
47
+ // ─── ABI parameter shapes ────────────────────────────────────────────────────
48
+ //
49
+ // Plain `as const` literals — no helper. A helper that takes `(name: string,
50
+ // type: string)` widens the literals before `as const` can pin them, which
51
+ // erases the type `decodeAbiParameters` needs to infer a tuple. Written out,
52
+ // abitype infers each payload's shape and the decode path below is cast-free.
53
+ // (`parseAbiParameters` is not an option: it rejects the nested tuples that
54
+ // LOGICAL_POLICY and FOR_EACH_POLICY require.)
55
+
56
+ const POLICY_SET_COMPONENTS = [
57
+ { name: 'templateId', type: 'string' },
58
+ { name: 'policy', type: 'bytes' },
59
+ { name: 'selector', type: 'bytes' },
60
+ ] as const
61
+
62
+ /**
63
+ * `templateId` → the ABI parameters of its `policy` bytes payload.
64
+ *
65
+ * Each entry is a one-element parameter list wrapping the payload tuple, so
66
+ * `decodeAbiParameters(codec, bytes)[0]` is the decoded struct.
67
+ */
68
+ export const policyCodecs = {
69
+ EAS_POLICY: [
70
+ {
71
+ name: 'policy',
72
+ type: 'tuple',
73
+ components: [
74
+ { name: 'easContract', type: 'address' },
75
+ { name: 'indexContract', type: 'address' },
76
+ { name: 'schemaUid', type: 'bytes32' },
77
+ ],
78
+ },
79
+ ],
80
+ DENYLIST_POLICY: [
81
+ { name: 'policy', type: 'tuple', components: [{ name: 'addresses', type: 'address[]' }] },
82
+ ],
83
+ VOLUME_POLICY: [
84
+ {
85
+ name: 'policy',
86
+ type: 'tuple',
87
+ components: [
88
+ { name: 'tokens', type: 'string[]' },
89
+ {
90
+ name: 'limits',
91
+ type: 'tuple[]',
92
+ components: [
93
+ { name: 'minLimit', type: 'uint256' },
94
+ { name: 'maxLimit', type: 'uint256' },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ ],
100
+ PERIODIC_VOLUME_POLICY: [
101
+ {
102
+ name: 'policy',
103
+ type: 'tuple',
104
+ components: [
105
+ { name: 'tokens', type: 'string[]' },
106
+ {
107
+ name: 'limits',
108
+ type: 'tuple[]',
109
+ components: [
110
+ { name: 'maxAmount', type: 'uint256' },
111
+ { name: 'resetPeriodSeconds', type: 'uint64' },
112
+ ],
113
+ },
114
+ ],
115
+ },
116
+ ],
117
+ OKRW_EAS_TRANSFER_LIMIT_POLICY: [
118
+ {
119
+ name: 'policy',
120
+ type: 'tuple',
121
+ components: [
122
+ { name: 'easContract', type: 'address' },
123
+ { name: 'indexContract', type: 'address' },
124
+ { name: 'schemaUid', type: 'bytes32' },
125
+ { name: 'transferLimitAmount', type: 'uint256' },
126
+ ],
127
+ },
128
+ ],
129
+ OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY: [
130
+ {
131
+ name: 'policy',
132
+ type: 'tuple',
133
+ components: [
134
+ { name: 'easContract', type: 'address' },
135
+ { name: 'indexContract', type: 'address' },
136
+ { name: 'schemaUid', type: 'bytes32' },
137
+ { name: 'maxAmount', type: 'uint256' },
138
+ { name: 'resetPeriodSeconds', type: 'uint64' },
139
+ ],
140
+ },
141
+ ],
142
+ AGENT_OKRW_TRANSFER_LIMIT_POLICY: [
143
+ { name: 'policy', type: 'tuple', components: [{ name: 'reserved', type: 'uint256' }] },
144
+ ],
145
+ LOGICAL_POLICY: [
146
+ {
147
+ name: 'policy',
148
+ type: 'tuple',
149
+ components: [
150
+ { name: 'quantifier', type: 'uint8' },
151
+ { name: 'children', type: 'tuple[]', components: POLICY_SET_COMPONENTS },
152
+ ],
153
+ },
154
+ ],
155
+ FOR_EACH_POLICY: [
156
+ {
157
+ name: 'policy',
158
+ type: 'tuple',
159
+ components: [
160
+ { name: 'quantifier', type: 'uint8' },
161
+ { name: 'subject', type: 'uint8' },
162
+ { name: 'child', type: 'tuple', components: POLICY_SET_COMPONENTS },
163
+ ],
164
+ },
165
+ ],
166
+ } as const
167
+
168
+ /** The input struct expected by {@link encodePolicy} for a given template. */
169
+ export type PolicyInput<id extends PolicyTemplateId> = AbiParametersToPrimitiveTypes<
170
+ (typeof policyCodecs)[id]
171
+ >[0]
172
+
173
+ // ─── Wire type ───────────────────────────────────────────────────────────────
174
+
175
+ /** A `PolicySet` exactly as the precompile returns it. */
176
+ export type RawPolicySet = {
177
+ templateId: string
178
+ policy: Hex
179
+ selector: Hex
180
+ }
181
+
182
+ // ─── Decoded tree ────────────────────────────────────────────────────────────
183
+
184
+ export type DecodedPolicy =
185
+ | { templateId: 'EAS_POLICY'; selector: Hex; easContract: Hex; indexContract: Hex; schemaUid: Hex }
186
+ | { templateId: 'DENYLIST_POLICY'; selector: Hex; addresses: readonly Hex[] }
187
+ | {
188
+ templateId: 'VOLUME_POLICY'
189
+ selector: Hex
190
+ limits: readonly { token: string; minLimit: bigint; maxLimit: bigint }[]
191
+ }
192
+ | {
193
+ templateId: 'PERIODIC_VOLUME_POLICY'
194
+ selector: Hex
195
+ limits: readonly { token: string; maxAmount: bigint; resetPeriodSeconds: bigint }[]
196
+ }
197
+ | {
198
+ templateId: 'OKRW_EAS_TRANSFER_LIMIT_POLICY'
199
+ selector: Hex
200
+ easContract: Hex
201
+ indexContract: Hex
202
+ schemaUid: Hex
203
+ transferLimitAmount: bigint
204
+ }
205
+ | {
206
+ templateId: 'OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY'
207
+ selector: Hex
208
+ easContract: Hex
209
+ indexContract: Hex
210
+ schemaUid: Hex
211
+ maxAmount: bigint
212
+ resetPeriodSeconds: bigint
213
+ }
214
+ /** The `reserved` field is ignored by the chain, so it is not surfaced. */
215
+ | { templateId: 'AGENT_OKRW_TRANSFER_LIMIT_POLICY'; selector: Hex }
216
+ | { templateId: 'LOGICAL_POLICY'; selector: Hex; quantifier: LogicalQuantifierName; children: DecodedPolicy[] }
217
+ | {
218
+ templateId: 'FOR_EACH_POLICY'
219
+ selector: Hex
220
+ quantifier: ForEachQuantifierName
221
+ subject: ForEachSubjectName
222
+ child: DecodedPolicy
223
+ }
224
+ /** Any template this SDK version does not model yet. Never throws. */
225
+ | { templateId: string; selector: Hex; raw: Hex; unknown: true }
226
+
227
+ const MAX_DEPTH = 8
228
+
229
+ const isKnown = (id: string): id is PolicyTemplateId =>
230
+ Object.prototype.hasOwnProperty.call(policyCodecs, id)
231
+
232
+ /**
233
+ * Recursively decode a `PolicySet` returned by `globalPolicies()` or
234
+ * `contractPolicies()` into a readable tree.
235
+ *
236
+ * Unrecognised template ids decode to `{ unknown: true, raw }` rather than
237
+ * throwing, so a chain upgrade that adds a policy type degrades a dApp's
238
+ * policy inspector instead of breaking it.
239
+ */
240
+ export function decodePolicySet(set: RawPolicySet, depth = 0): DecodedPolicy {
241
+ if (depth > MAX_DEPTH) throw new Error(`policy nesting exceeded depth ${MAX_DEPTH}`)
242
+
243
+ const { selector } = set
244
+ if (!isKnown(set.templateId))
245
+ return { templateId: set.templateId, selector, raw: set.policy, unknown: true }
246
+
247
+ // Each branch indexes its codec by literal key, so `decodeAbiParameters`
248
+ // infers the exact payload tuple — no casts.
249
+ switch (set.templateId) {
250
+ case 'EAS_POLICY': {
251
+ const [p] = decodeAbiParameters(policyCodecs.EAS_POLICY, set.policy)
252
+ return {
253
+ templateId: 'EAS_POLICY',
254
+ selector,
255
+ easContract: p.easContract,
256
+ indexContract: p.indexContract,
257
+ schemaUid: p.schemaUid,
258
+ }
259
+ }
260
+ case 'DENYLIST_POLICY': {
261
+ const [p] = decodeAbiParameters(policyCodecs.DENYLIST_POLICY, set.policy)
262
+ return { templateId: 'DENYLIST_POLICY', selector, addresses: p.addresses }
263
+ }
264
+ case 'VOLUME_POLICY': {
265
+ const [p] = decodeAbiParameters(policyCodecs.VOLUME_POLICY, set.policy)
266
+ // tokens[i] pairs with limits[i], but the ABI does not tie the array
267
+ // lengths — a mis-encoded policy must degrade to raw, not throw.
268
+ if (p.tokens.length !== p.limits.length)
269
+ return { templateId: set.templateId, selector, raw: set.policy, unknown: true }
270
+ return {
271
+ templateId: 'VOLUME_POLICY',
272
+ selector,
273
+ limits: p.tokens.map((token, i) => ({
274
+ token,
275
+ minLimit: p.limits[i].minLimit,
276
+ maxLimit: p.limits[i].maxLimit,
277
+ })),
278
+ }
279
+ }
280
+ case 'PERIODIC_VOLUME_POLICY': {
281
+ const [p] = decodeAbiParameters(policyCodecs.PERIODIC_VOLUME_POLICY, set.policy)
282
+ if (p.tokens.length !== p.limits.length)
283
+ return { templateId: set.templateId, selector, raw: set.policy, unknown: true }
284
+ return {
285
+ templateId: 'PERIODIC_VOLUME_POLICY',
286
+ selector,
287
+ limits: p.tokens.map((token, i) => ({
288
+ token,
289
+ maxAmount: p.limits[i].maxAmount,
290
+ resetPeriodSeconds: p.limits[i].resetPeriodSeconds,
291
+ })),
292
+ }
293
+ }
294
+ case 'OKRW_EAS_TRANSFER_LIMIT_POLICY': {
295
+ const [p] = decodeAbiParameters(policyCodecs.OKRW_EAS_TRANSFER_LIMIT_POLICY, set.policy)
296
+ return {
297
+ templateId: 'OKRW_EAS_TRANSFER_LIMIT_POLICY',
298
+ selector,
299
+ easContract: p.easContract,
300
+ indexContract: p.indexContract,
301
+ schemaUid: p.schemaUid,
302
+ transferLimitAmount: p.transferLimitAmount,
303
+ }
304
+ }
305
+ case 'OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY': {
306
+ const [p] = decodeAbiParameters(policyCodecs.OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY, set.policy)
307
+ return {
308
+ templateId: 'OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY',
309
+ selector,
310
+ easContract: p.easContract,
311
+ indexContract: p.indexContract,
312
+ schemaUid: p.schemaUid,
313
+ maxAmount: p.maxAmount,
314
+ resetPeriodSeconds: p.resetPeriodSeconds,
315
+ }
316
+ }
317
+ case 'AGENT_OKRW_TRANSFER_LIMIT_POLICY': {
318
+ // Decode to validate the payload layout; `reserved` itself is ignored
319
+ // by the chain and not surfaced.
320
+ decodeAbiParameters(policyCodecs.AGENT_OKRW_TRANSFER_LIMIT_POLICY, set.policy)
321
+ return { templateId: 'AGENT_OKRW_TRANSFER_LIMIT_POLICY', selector }
322
+ }
323
+ case 'LOGICAL_POLICY': {
324
+ const [p] = decodeAbiParameters(policyCodecs.LOGICAL_POLICY, set.policy)
325
+ return {
326
+ templateId: 'LOGICAL_POLICY',
327
+ selector,
328
+ quantifier: nameOf(LogicalQuantifier, p.quantifier),
329
+ children: p.children.map((c) => decodePolicySet(c, depth + 1)),
330
+ }
331
+ }
332
+ case 'FOR_EACH_POLICY': {
333
+ const [p] = decodeAbiParameters(policyCodecs.FOR_EACH_POLICY, set.policy)
334
+ return {
335
+ templateId: 'FOR_EACH_POLICY',
336
+ selector,
337
+ quantifier: nameOf(ForEachQuantifier, p.quantifier),
338
+ subject: nameOf(ForEachSubject, p.subject),
339
+ child: decodePolicySet(p.child, depth + 1),
340
+ }
341
+ }
342
+ // Unreachable while the switch covers every id in `policyCodecs`; kept so
343
+ // a codec added without a decode branch degrades instead of crashing.
344
+ default:
345
+ return { templateId: set.templateId, selector, raw: set.policy, unknown: true }
346
+ }
347
+ }
348
+
349
+ /**
350
+ * ABI-encode a policy payload for its template.
351
+ *
352
+ * The `policy` argument is typed per `templateId` via {@link PolicyInput}, so
353
+ * the public signature is fully checked. The single internal cast is the
354
+ * unavoidable one: indexing `policyCodecs` by a generic `id` yields a union
355
+ * that `encodeAbiParameters` cannot unify positionally (viem does the same at
356
+ * this boundary).
357
+ *
358
+ * Admin-facing: only `policyAdmin` may set global policies, and only a
359
+ * contract's registered admin may change its contract policies.
360
+ */
361
+ export function encodePolicy<id extends PolicyTemplateId>(templateId: id, policy: PolicyInput<id>): Hex {
362
+ const codec = policyCodecs[templateId]
363
+ return encodeAbiParameters(codec, [policy] as never)
364
+ }
365
+
366
+ /**
367
+ * Normalize a function selector for a `PolicySet`.
368
+ *
369
+ * The chain accepts either an empty value (wildcard, stored internally as
370
+ * `"__all__"`) or exactly 4 bytes, lowercased. Anything else is rejected with
371
+ * `InvalidSelector`.
372
+ */
373
+ export function normalizeSelector(selector?: Hex | undefined): Hex {
374
+ if (!selector || selector === '0x') return '0x'
375
+ const body = selector.slice(2).toLowerCase()
376
+ // Length alone is not enough: a non-hex body would pass here and only be
377
+ // rejected chain-side with an opaque InvalidSelector after gas was spent.
378
+ if (!/^[0-9a-f]{8}$/.test(body))
379
+ throw new Error(`selector must be 4 bytes of hex (8 hex chars), got "${selector}"`)
380
+ return `0x${body}`
381
+ }
@@ -0,0 +1,57 @@
1
+ import { type Address, maxUint256 } from 'viem'
2
+
3
+ /**
4
+ * Maroo static precompile addresses.
5
+ *
6
+ * Mirrors the `*_PRECOMPILED_ADDRESS` constants in
7
+ * `@maroo-chain/contracts/precompiles/{okrw,pcl,eas,agent}/I*.sol`.
8
+ *
9
+ * The contracts package does not export these from its TypeScript build
10
+ * (only the `.sol` sources carry them), so we redeclare them here.
11
+ * `test/addresses.test.ts` asserts these still match the installed package.
12
+ */
13
+ export const precompileAddresses = {
14
+ okrw: '0x1000000000000000000000000000000000000001',
15
+ pcl: '0x1000000000000000000000000000000000000005',
16
+ /** Returns `eas` module params. Not the EAS contract itself — see {@link easContracts}. */
17
+ eas: '0x1000000000000000000000000000000000000009',
18
+ agent: '0x100000000000000000000000000000000000000A',
19
+ /** Clairveil shielded pool. Not yet deployed on marooTestnet. */
20
+ privacy: '0x100000000000000000000000000000000000000b',
21
+ } as const satisfies Record<string, Address>
22
+
23
+ /**
24
+ * EAS contracts, as reported by `eas.getParams()` on marooTestnet.
25
+ *
26
+ * These sit at precompile-shaped addresses but implement the standard
27
+ * Ethereum Attestation Service interfaces. `@maroo-chain/contracts@0.0.6`
28
+ * ships no ABI for them; use `@ethereum-attestation-service/eas-contracts`.
29
+ *
30
+ * Do not hardcode these in application code — read them from
31
+ * `client.eas.getParams()`, which is authoritative per network.
32
+ */
33
+ export const easContracts = {
34
+ schemaRegistry: '0x1000000000000000000000000000000000000006',
35
+ eas: '0x1000000000000000000000000000000000000007',
36
+ indexer: '0x1000000000000000000000000000000000000008',
37
+ } as const satisfies Record<string, Address>
38
+
39
+ /**
40
+ * ABI entries that exist for chain-side codegen or proxy plumbing and must
41
+ * never be surfaced as client actions.
42
+ *
43
+ * - `_policies` is a `pure` no-op declared solely so the Solidity compiler
44
+ * emits the policy structs into the ABI (see the comment at the bottom of
45
+ * `IPcl.sol`). Calling it does nothing.
46
+ * - `preCall`/`postCall` are invoked by the PCL proxy hook with a `principal`
47
+ * the caller cannot forge. Application code goes through {@link runOnPcl}.
48
+ */
49
+ export const excludedFunctions = {
50
+ pcl: ['_policies', 'preCall', 'postCall'],
51
+ } as const
52
+
53
+ /** The selector wildcard. An empty `selector` matches every function. */
54
+ export const SELECTOR_ALL = '0x' as const
55
+
56
+ /** `VolumeUnitPolicy.maxLimit` sentinel meaning "no upper bound". */
57
+ export const NO_MAX_LIMIT = maxUint256