maroo-viem-poc 0.1.0 → 0.1.1
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 +68 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# maroo-viem-poc
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A viem extension for the Maroo chain — **proof of concept**. It exposes Maroo's
|
|
4
|
+
four precompiles (pcl / okrw / eas / agent) as viem client decorators and fills
|
|
5
|
+
in what the ABI alone cannot tell you: PCL policy encoding, the `runOnPcl`
|
|
6
|
+
routing rule, and what each of the 40 policy-violation errors actually means.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
Everything is **verified against live marooTestnet (chainId 450815)** — reads,
|
|
9
|
+
revert decoding, and signed transactions. 41 unit tests plus 22 live tests back
|
|
10
|
+
that claim.
|
|
9
11
|
|
|
10
12
|
```bash
|
|
11
13
|
npm i maroo-viem-poc viem
|
|
@@ -15,13 +17,13 @@ npm i maroo-viem-poc viem
|
|
|
15
17
|
|
|
16
18
|
```ts
|
|
17
19
|
import { createPublicClient, createWalletClient, http } from 'viem'
|
|
18
|
-
import { marooTestnet } from 'viem/chains' // viem 2.55+
|
|
20
|
+
import { marooTestnet } from 'viem/chains' // shipped in viem 2.55+
|
|
19
21
|
import { marooPublicActions, marooWalletActions } from 'maroo-viem-poc'
|
|
20
22
|
|
|
21
23
|
const client = createPublicClient({ chain: marooTestnet, transport: http() })
|
|
22
24
|
.extend(marooPublicActions())
|
|
23
25
|
|
|
24
|
-
//
|
|
26
|
+
// All four precompiles expose getParams, so namespacing is mandatory.
|
|
25
27
|
const { policyAdmin, entrypoints } = await client.pcl.getParams()
|
|
26
28
|
const { minter, mintDenom } = await client.okrw.getParams() // mintDenom: 'atokrw'
|
|
27
29
|
const { schemaRegistry, eas, indexer } = await client.eas.getParams()
|
|
@@ -30,18 +32,20 @@ const [agentIds] = await client.agent.getAgentIds({
|
|
|
30
32
|
})
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
##
|
|
35
|
+
## Why the ABI alone is not enough
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
This is why the package exists — and what you must know before building a dApp
|
|
38
|
+
on Maroo.
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
**Contract-scoped policies only apply through `runOnPcl`.** Calling a target
|
|
41
|
+
contract directly checks global policies only; contract-scoped policies are
|
|
42
|
+
**silently skipped**. No error, no event on the failure path.
|
|
39
43
|
|
|
40
44
|
```ts
|
|
41
45
|
const wallet = createWalletClient({ account, chain: marooTestnet, transport: http() })
|
|
42
46
|
.extend(marooWalletActions())
|
|
43
47
|
|
|
44
|
-
//
|
|
48
|
+
// Same shape as writeContract — switching a call site is a one-word change.
|
|
45
49
|
const hash = await wallet.pcl.runOnPcl({
|
|
46
50
|
address: token,
|
|
47
51
|
abi: erc20Abi,
|
|
@@ -49,19 +53,21 @@ const hash = await wallet.pcl.runOnPcl({
|
|
|
49
53
|
args: [recipient, amount],
|
|
50
54
|
})
|
|
51
55
|
|
|
52
|
-
//
|
|
56
|
+
// Native transfers can be policy-checked too.
|
|
53
57
|
await wallet.pcl.runOnPcl({ address: recipient, value: parseEther('1') })
|
|
54
58
|
```
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
**Attaching `msg.value` to `runOnPcl` strands your funds.** The forwarded value
|
|
61
|
+
rides in the *argument* — the chain debits the caller directly inside
|
|
62
|
+
`evm.Call(caller, target, data, gas, value)` — while outer-tx `msg.value` is
|
|
63
|
+
neither forwarded nor refunded and accumulates at the precompile address
|
|
64
|
+
forever (over 120k tOKRW is already stuck at `0x…0005` on testnet this way).
|
|
65
|
+
This SDK **blocks that path at both the type level and at runtime**.
|
|
61
66
|
|
|
62
|
-
**`PolicySet.policy
|
|
63
|
-
`bytes
|
|
64
|
-
policy
|
|
67
|
+
**`PolicySet.policy` is not self-describing.** The `templateId` string decides
|
|
68
|
+
the layout of the opaque `bytes`, and that mapping exists nowhere in the ABI.
|
|
69
|
+
This package's policy codec *is* the mapping — all 9 templates including the
|
|
70
|
+
recursive ones, pinned against live chain bytes.
|
|
65
71
|
|
|
66
72
|
```ts
|
|
67
73
|
import { decodePolicySet } from 'maroo-viem-poc'
|
|
@@ -69,12 +75,12 @@ import { decodePolicySet } from 'maroo-viem-poc'
|
|
|
69
75
|
const { policies } = await client.pcl.globalPolicies()
|
|
70
76
|
const tree = policies.map((p) => decodePolicySet(p))
|
|
71
77
|
// FOR_EACH(Every of AgentOwners) → LOGICAL(Or) → [PERIODIC_VOLUME 10M/24h, EAS …]
|
|
72
|
-
//
|
|
78
|
+
// Unknown templateIds degrade to { unknown: true, raw } instead of throwing.
|
|
73
79
|
```
|
|
74
80
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
`resetAt
|
|
81
|
+
**Policy-violation reverts should be human-readable.** viem decodes the error
|
|
82
|
+
name and args once it has the ABI, but it cannot tell you which of the 40
|
|
83
|
+
errors mean "a policy rejected this user" — or when `resetAt` is.
|
|
78
84
|
|
|
79
85
|
```ts
|
|
80
86
|
import { decodePclError } from 'maroo-viem-poc'
|
|
@@ -82,16 +88,16 @@ import { decodePclError } from 'maroo-viem-poc'
|
|
|
82
88
|
try {
|
|
83
89
|
await wallet.pcl.runOnPcl({ ... })
|
|
84
90
|
} catch (err) {
|
|
85
|
-
const v = decodePclError(err) //
|
|
91
|
+
const v = decodePclError(err) // null for non-PCL errors (selector-gated)
|
|
86
92
|
if (v?.kind === 'periodic-volume')
|
|
87
|
-
console.log(`24h
|
|
93
|
+
console.log(`24h limit hit — resets at ${v.resetAt.toLocaleString()}`)
|
|
88
94
|
if (v?.kind === 'any-of-rejected')
|
|
89
|
-
console.log('
|
|
95
|
+
console.log('every branch of the OR policy rejected:', v.children) // recursive, depth-capped
|
|
90
96
|
}
|
|
91
97
|
```
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
**Batch reads with `.call()`.** Every read action also exposes `.call()`, which
|
|
100
|
+
builds a multicall/simulate descriptor instead of executing.
|
|
95
101
|
|
|
96
102
|
```ts
|
|
97
103
|
const volumes = await client.multicall({
|
|
@@ -99,47 +105,52 @@ const volumes = await client.multicall({
|
|
|
99
105
|
client.pcl.globalOkrwEasPeriodicVolume.call({ args: [u] })),
|
|
100
106
|
allowFailure: false,
|
|
101
107
|
})
|
|
102
|
-
// PeriodicVolume
|
|
103
|
-
// (
|
|
108
|
+
// PeriodicVolume has FOUR fields: amount · maxAmount · resetPeriodSeconds · resetAt
|
|
109
|
+
// (decoding only the first two happens to work by accident — and silently
|
|
110
|
+
// loses the reset schedule)
|
|
104
111
|
```
|
|
105
112
|
|
|
106
|
-
##
|
|
113
|
+
## Chain quirks worth knowing
|
|
107
114
|
|
|
108
|
-
-
|
|
109
|
-
- testnet
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
115
|
+
- Precompiles return `0x` from `eth_getCode`. Do not gate on "is a contract deployed here" preflights.
|
|
116
|
+
- The testnet native denom is `atokrw`; mainnet uses `aokrw`. `VOLUME_POLICY.tokens[]` is keyed by denom string, so hardcoding one breaks on the other network.
|
|
117
|
+
- The template id is `FOR_EACH_POLICY` — not `FOREACH_POLICY`. The underscore is load-bearing.
|
|
118
|
+
- Some PCL error arguments return addresses in bech32 (`maroo1…`), which cannot be compared to hex addresses directly.
|
|
119
|
+
- Much of the write surface is permission-tiered: `okrw.mint` is minter-only (a chain param), `pcl.setGlobalPolicies` / `registerPolicyTemplate` are `policyAdmin`-only, and `changeContractPolicies` is restricted to the admin fixed at registration time.
|
|
113
120
|
|
|
114
121
|
## API
|
|
115
122
|
|
|
116
|
-
| Export |
|
|
123
|
+
| Export | Purpose |
|
|
117
124
|
|---|---|
|
|
118
|
-
| `marooPublicActions()` | `client.{pcl,okrw,eas,agent}.*`
|
|
119
|
-
| `marooWalletActions()` | `client.pcl.runOnPcl`
|
|
120
|
-
| `decodePolicySet` / `encodePolicy` | PCL
|
|
121
|
-
| `decodePclError` | revert → `PclViolation`
|
|
122
|
-
| `precompileAddresses` / `easContracts` |
|
|
123
|
-
| `precompileActions(abi, addr)` |
|
|
124
|
-
| `normalizeSelector` / `SELECTOR_ALL` / `NO_MAX_LIMIT` |
|
|
125
|
+
| `marooPublicActions()` | `client.{pcl,okrw,eas,agent}.*` reads + `.call()` descriptors |
|
|
126
|
+
| `marooWalletActions()` | `client.pcl.runOnPcl` wrapper + writes (see JSDoc for permission tiers) |
|
|
127
|
+
| `decodePolicySet` / `encodePolicy` | PCL policy codec (9 templates, recursive, live-verified) |
|
|
128
|
+
| `decodePclError` | revert → `PclViolation` union; `null` for foreign errors |
|
|
129
|
+
| `precompileAddresses` / `easContracts` | address constants, pinned by drift-guard tests |
|
|
130
|
+
| `precompileActions(abi, addr)` | typed action factory for any `as const` ABI |
|
|
131
|
+
| `normalizeSelector` / `SELECTOR_ALL` / `NO_MAX_LIMIT` | policy selector / sentinel utilities |
|
|
125
132
|
|
|
126
|
-
##
|
|
133
|
+
## Testing
|
|
127
134
|
|
|
128
135
|
```bash
|
|
129
|
-
pnpm test #
|
|
130
|
-
pnpm test:live #
|
|
131
|
-
MAROO_TEST_PRIVATE_KEY=0x… pnpm test:live #
|
|
136
|
+
pnpm test # unit — no network required
|
|
137
|
+
pnpm test:live # live marooTestnet reads / revert decoding (no gas needed)
|
|
138
|
+
MAROO_TEST_PRIVATE_KEY=0x… pnpm test:live # signed write paths too (needs tOKRW)
|
|
132
139
|
```
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
precompile
|
|
141
|
+
Address and ABI drift guards are included: if a `@maroo-chain/contracts` bump
|
|
142
|
+
moves a precompile or changes a function signature the SDK relies on, unit
|
|
143
|
+
tests fail loudly.
|
|
136
144
|
|
|
137
|
-
##
|
|
145
|
+
## Out of scope (deliberately)
|
|
138
146
|
|
|
139
|
-
- **privacy precompile (`0x…000b`)** —
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
-
|
|
147
|
+
- **The privacy precompile (`0x…000b`)** — that is the clairveil SDK's
|
|
148
|
+
responsibility; only the address constant is exposed here. (Not yet deployed
|
|
149
|
+
on testnet.)
|
|
150
|
+
- **Custom tx types / formatters / serializers** — Maroo uses standard EVM
|
|
151
|
+
transactions, so none are needed.
|
|
152
|
+
- **A wagmi hooks layer** — a separate package once the viem layer settles.
|
|
153
|
+
- This is a **PoC**: unaudited, and the API may change without notice.
|
|
143
154
|
|
|
144
155
|
## License
|
|
145
156
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maroo-viem-poc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
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
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|