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/README.md +146 -0
- package/dist/index.cjs +740 -0
- package/dist/index.d.cts +3637 -0
- package/dist/index.d.mts +3637 -0
- package/dist/index.mjs +717 -0
- package/package.json +59 -0
- package/src/actions/runOnPcl.ts +117 -0
- package/src/codec/policy.ts +381 -0
- package/src/constants.ts +57 -0
- package/src/core/precompileActions.ts +215 -0
- package/src/decorators/maroo.ts +114 -0
- package/src/decorators/precompiles.ts +44 -0
- package/src/errors/pcl.ts +137 -0
- package/src/index.ts +58 -0
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;
|