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.
Files changed (2) hide show
  1. package/README.md +68 -57
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # maroo-viem-poc
2
2
 
3
- Maroo 체인용 viem 확장 **PoC**입니다. Maroo precompile 4종(pcl / okrw / eas / agent)을
4
- viem 클라이언트 데코레이터로 노출하고, ABI만으로는 없는 것들(PCL 정책 인코딩,
5
- `runOnPcl` 경유 규칙, 40종 정책 위반 에러의 의미)을 SDK 계층에서 채웁니다.
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
- 모든 동작은 **살아있는 marooTestnet(chainId 450815) 대해 검증**되었습니다 —
8
- 읽기·revert 디코딩·서명 트랜잭션 전부요. 유닛 41 + 라이브 22 테스트가 근거입니다.
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
- // precompile 모두 getParams 노출하므로 네임스페이스가 필수입니다.
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
- ## ABI만으로 부족한가
35
+ ## Why the ABI alone is not enough
34
36
 
35
- 패키지가 존재하는 이유이자, dApp을 만들 반드시 알아야 하는 체인 특성입니다.
37
+ This is why the package exists and what you must know before building a dApp
38
+ on Maroo.
36
39
 
37
- **`runOnPcl`을 경유해야 컨트랙트 정책이 적용됩니다.** 타깃 컨트랙트를 직접 호출하면
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
- // writeContract와 같은 모양 단어만 바꾸면 정책이 적용됩니다.
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
- **`runOnPcl`에 `msg.value`를 붙이면 돈이 사라집니다.** 전달할 값은 *인자*로 갑니다
57
- 체인이 `evm.Call(caller, target, data, gas, value)`로 호출자에게서 직접 차감하며,
58
- 바깥 트랜잭션의 `msg.value`는 포워딩도 환불도 되지 않고 precompile 주소에 영구히
59
- 남습니다(testnet의 `0x…0005`에 이미 12만+ tOKRW가 이렇게 잠겨 있습니다).
60
- SDK는 **타입과 런타임 양쪽에서 경로를 차단**합니다.
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`는 self-describing 아닙니다.** `templateId` 문자열이 opaque
63
- `bytes`의 레이아웃을 결정하는데, 매핑은 ABI 어디에도 없습니다. 이 SDK의
64
- policy 코덱이 매핑입니다(중첩 포함 9종 전부, 라이브 체인 바이트로 검증).
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
- // 모르는 templateId는 throw 대신 { unknown: true, raw } 강등됩니다.
78
+ // Unknown templateIds degrade to { unknown: true, raw } instead of throwing.
73
79
  ```
74
80
 
75
- **정책 위반 revert는 사람이 읽을 있어야 합니다.** viem이 에러 이름·인자까지는
76
- 디코딩해 주지만, 40종 어떤 것이 "정책이 사용자를 거부했다"는 뜻인지,
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) // PCL 소유가 아닌 에러면 null (셀렉터 기준 판정)
91
+ const v = decodePclError(err) // null for non-PCL errors (selector-gated)
86
92
  if (v?.kind === 'periodic-volume')
87
- console.log(`24h 한도 초과 — ${v.resetAt.toLocaleString()}에 리셋`)
93
+ console.log(`24h limit hitresets at ${v.resetAt.toLocaleString()}`)
88
94
  if (v?.kind === 'any-of-rejected')
89
- console.log('OR 정책의 모든 분기가 거부:', v.children) // 재귀 디코딩, 깊이 상한 8
95
+ console.log('every branch of the OR policy rejected:', v.children) // recursive, depth-capped
90
96
  }
91
97
  ```
92
98
 
93
- **배치 조회는 `.call()`로.** 모든 read 액션은 실행하지 않고 multicall/simulate용
94
- 디스크립터만 만드는 `.call()`을 함께 노출합니다.
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 4필드입니다: amount · maxAmount · resetPeriodSeconds · resetAt
103
- // ( 2필드만 디코딩해도 우연히 맞는 값이 나오므로 주의리셋 시각을 잃습니다)
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
- - precompile은 `eth_getCode`가 `0x`를 반환합니다. "컨트랙트 배포됐나" 프리플라이트로 걸러내면 됩니다.
109
- - testnet 네이티브 denom `atokrw`, mainnet `aokrw`. `VOLUME_POLICY.tokens[]`가 denom 문자열 키라 하드코딩하면 한쪽에서 깨집니다.
110
- - 정책 템플릿 id `FOR_EACH_POLICY`입니다 — `FOREACH_POLICY`가 아닙니다.
111
- - 일부 PCL 에러 인자는 주소를 bech32(`maroo1…`) 반환합니다. hex와 직접 비교할 없습니다.
112
- - 쓰기 표면의 다수는 권한 계층이 있습니다: `okrw.mint`는 체인 파라미터의 minter만, `pcl.setGlobalPolicies`/`registerPolicyTemplate`은 `policyAdmin`만, `changeContractPolicies`는 등록 지정한 admin 호출할 있습니다.
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}.*` 읽기 + `.call()` 디스크립터 |
119
- | `marooWalletActions()` | `client.pcl.runOnPcl` 래퍼 + 쓰기 액션(권한 계층 JSDoc 참고) |
120
- | `decodePolicySet` / `encodePolicy` | PCL 정책 코덱 (9종, 재귀, 라이브 검증) |
121
- | `decodePclError` | revert → `PclViolation` 유니언. 남의 에러는 `null` |
122
- | `precompileAddresses` / `easContracts` | 주소 상수 (드리프트 가드 테스트로 고정) |
123
- | `precompileActions(abi, addr)` | 임의 `as const` ABI → 타입드 액션 팩토리 |
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 # 라이브 marooTestnet 읽기/revert 검증 (가스 불필요)
131
- MAROO_TEST_PRIVATE_KEY=0x… pnpm test:live # 서명 쓰기 경로까지 (tOKRW 필요)
136
+ pnpm test # unitno 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
- 주소·ABI 드리프트 가드가 포함되어 있습니다: `@maroo-chain/contracts`를 올렸을 때
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`)** — clairveil SDK의 책임입니다. 주소 상수만 노출합니다. (testnet 미배포)
140
- - **커스텀 tx 타입 / formatters / serializers** Maroo는 표준 EVM 트랜잭션만 사용하므로 필요 없습니다.
141
- - **wagmi 훅 계층** — viem 계층이 안정된 뒤 별도 패키지로.
142
- - 이것은 **PoC입니다.** 감사받지 않았고, API는 예고 없이 바뀝니다.
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.0",
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,