@chainfoundry/chaincodec 0.1.0 → 0.1.2

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 ADDED
@@ -0,0 +1,380 @@
1
+ # @chainfoundry/chaincodec
2
+
3
+ Universal blockchain ABI decoder for Node.js — production-grade EVM event & function call decoding.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@chainfoundry/chaincodec)](https://www.npmjs.com/package/@chainfoundry/chaincodec)
6
+ [![license](https://img.shields.io/npm/l/@chainfoundry/chaincodec)](LICENSE)
7
+
8
+ Native Node.js bindings (via [napi-rs](https://napi.rs)) for the [chaincodec](https://crates.io/crates/chaincodec-evm) Rust library. Decode `eth_getLogs` entries, function calldata, and compute topic0 fingerprints — all at Rust speed with a TypeScript-first API.
9
+
10
+ ---
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @chainfoundry/chaincodec
16
+ # or
17
+ yarn add @chainfoundry/chaincodec
18
+ # or
19
+ pnpm add @chainfoundry/chaincodec
20
+ ```
21
+
22
+ No build step required — pre-built native binaries are bundled for all major platforms.
23
+
24
+ ---
25
+
26
+ ## Platform support
27
+
28
+ | Platform | Architecture | Supported |
29
+ | --- | --- | --- |
30
+ | Linux (glibc) | x64 | ✅ |
31
+ | Linux (glibc) | arm64 | ✅ |
32
+ | Linux (musl / Alpine) | x64 | ✅ |
33
+ | macOS | x64 (Intel) | ✅ |
34
+ | macOS | arm64 (Apple Silicon) | ✅ |
35
+ | Windows | x64 | ✅ |
36
+
37
+ ---
38
+
39
+ ## Quick start — decode an EVM event log
40
+
41
+ ```typescript
42
+ import { EvmDecoder, MemoryRegistry } from '@chainfoundry/chaincodec';
43
+
44
+ // 1. Load your schemas (CSDL YAML format)
45
+ const registry = new MemoryRegistry();
46
+
47
+ // Load from inline CSDL string
48
+ registry.loadCsdl(`
49
+ schema ERC20Transfer:
50
+ version: 1
51
+ description: "ERC-20 standard Transfer event"
52
+ chains: [ethereum, arbitrum, base, polygon, optimism]
53
+ event: Transfer
54
+ fingerprint: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
55
+ fields:
56
+ from: { type: address, indexed: true }
57
+ to: { type: address, indexed: true }
58
+ value: { type: uint256, indexed: false }
59
+ meta:
60
+ protocol: erc20
61
+ category: token
62
+ `);
63
+
64
+ // Or load from files on disk
65
+ // registry.loadFile('./schemas/erc20.csdl');
66
+ // registry.loadDirectory('./schemas');
67
+
68
+ // 2. Decode a raw log from eth_getLogs
69
+ const decoder = new EvmDecoder();
70
+
71
+ const rawLog = {
72
+ chain: 'ethereum',
73
+ txHash: '0xabc123...',
74
+ blockNumber: 19500000,
75
+ blockTimestamp: 1710000000,
76
+ logIndex: 0,
77
+ address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
78
+ topics: [
79
+ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer
80
+ '0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045', // from
81
+ '0x000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b', // to
82
+ ],
83
+ data: '0x00000000000000000000000000000000000000000000000000000000000f4240',
84
+ };
85
+
86
+ const event = decoder.decodeEvent(rawLog, registry);
87
+
88
+ console.log(event.schema); // "ERC20Transfer"
89
+ console.log(event.fields.from); // { type: 'address', value: '0xd8da6bf2...' }
90
+ console.log(event.fields.to); // { type: 'address', value: '0xab5801a7...' }
91
+ console.log(event.fields.value); // { type: 'biguint', value: '1000000' }
92
+ console.log(event.fingerprint); // "0xddf252ad..."
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Decode function calldata
98
+
99
+ ```typescript
100
+ import { EvmCallDecoder } from '@chainfoundry/chaincodec';
101
+ import { readFileSync } from 'fs';
102
+
103
+ // Load ABI JSON (from Etherscan, Hardhat artifacts, Foundry out/, etc.)
104
+ const abiJson = readFileSync('./abi/erc20.json', 'utf-8');
105
+ const decoder = EvmCallDecoder.fromAbiJson(abiJson);
106
+
107
+ // Decode raw calldata from a transaction's `input` field
108
+ const calldata = '0xa9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045000000000000000000000000000000000000000000000000000000000000000a';
109
+ const call = decoder.decodeCall(calldata);
110
+
111
+ console.log(call.functionName); // "transfer"
112
+ console.log(call.selector); // "0xa9059cbb"
113
+
114
+ for (const [name, value] of call.inputs) {
115
+ console.log(` ${name}:`, value);
116
+ }
117
+ // to: { type: 'address', value: '0xd8da6bf2...' }
118
+ // amount: { type: 'biguint', value: '10' }
119
+
120
+ // List all functions in the ABI
121
+ console.log(decoder.functionNames()); // ['transfer', 'approve', ...]
122
+ console.log(decoder.selectorFor('transfer')); // "0xa9059cbb"
123
+ ```
124
+
125
+ ---
126
+
127
+ ## ABI encode a function call
128
+
129
+ `encodeCall` takes `argsJson` — a JSON string of `NormalizedValue[]`.
130
+ Each `NormalizedValue` has `{ type, value }` matching the Solidity type.
131
+
132
+ ```typescript
133
+ import { EvmEncoder } from '@chainfoundry/chaincodec';
134
+
135
+ const abiJson = JSON.stringify([{
136
+ name: 'transfer',
137
+ type: 'function',
138
+ inputs: [
139
+ { name: 'to', type: 'address' },
140
+ { name: 'amount', type: 'uint256' },
141
+ ],
142
+ outputs: [{ name: '', type: 'bool' }],
143
+ stateMutability: 'nonpayable',
144
+ }]);
145
+
146
+ const encoder = EvmEncoder.fromAbiJson(abiJson);
147
+
148
+ const calldata = encoder.encodeCall(
149
+ 'transfer',
150
+ JSON.stringify([
151
+ { type: 'address', value: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' },
152
+ { type: 'uint', value: 1000000 },
153
+ ])
154
+ );
155
+
156
+ console.log(calldata);
157
+ // "0xa9059cbb000000000000000000000000d8da6bf2...000f4240"
158
+ // First 4 bytes (0xa9059cbb) = transfer(address,uint256) selector
159
+ ```
160
+
161
+ ### NormalizedValue input types for encoding
162
+
163
+ | Solidity type | `NormalizedValue` JSON |
164
+ | --- | --- |
165
+ | `address` | `{ "type": "address", "value": "0x..." }` |
166
+ | `uint256` | `{ "type": "uint", "value": 1000000 }` |
167
+ | `uint256` (large) | `{ "type": "biguint", "value": "99999999999999999999" }` |
168
+ | `int256` | `{ "type": "int", "value": -100 }` |
169
+ | `bool` | `{ "type": "bool", "value": true }` |
170
+ | `bytes` | `{ "type": "bytes", "value": [0xde, 0xad, 0xbe, 0xef] }` |
171
+ | `string` | `{ "type": "str", "value": "hello" }` |
172
+ | `address[]` | `{ "type": "array", "value": [{ "type": "address", "value": "0x..." }] }` |
173
+
174
+ ---
175
+
176
+ ## Batch decode
177
+
178
+ Decode thousands of logs in parallel using Rayon (Rust's parallel iterator):
179
+
180
+ ```typescript
181
+ import { EvmDecoder, MemoryRegistry } from '@chainfoundry/chaincodec';
182
+
183
+ const registry = new MemoryRegistry();
184
+ registry.loadCsdl(/* your CSDL schemas */);
185
+
186
+ const decoder = new EvmDecoder();
187
+
188
+ // Decode a batch of raw logs — returns { events, errors }
189
+ const { events, errors } = decoder.decodeBatch(rawLogs, registry);
190
+
191
+ console.log(`decoded: ${events.length} events`);
192
+ console.log(`errors: ${errors.length}`);
193
+
194
+ for (const event of events) {
195
+ console.log(event.schema, event.fields);
196
+ }
197
+
198
+ for (const { index, error } of errors) {
199
+ console.warn(`log[${index}] failed: ${error}`);
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Compute topic0 fingerprint
206
+
207
+ ```typescript
208
+ const decoder = new EvmDecoder();
209
+
210
+ const fp = decoder.fingerprint({
211
+ chain: 'ethereum',
212
+ txHash: '0x0',
213
+ blockNumber: 0,
214
+ blockTimestamp: 0,
215
+ logIndex: 0,
216
+ address: '0x0000000000000000000000000000000000000000',
217
+ topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'],
218
+ data: '0x',
219
+ });
220
+ console.log(fp); // "0xddf252ad..."
221
+ ```
222
+
223
+ ---
224
+
225
+ ## EIP-712 typed data
226
+
227
+ ```typescript
228
+ import { Eip712Parser } from '@chainfoundry/chaincodec';
229
+
230
+ const parser = new Eip712Parser();
231
+
232
+ const typedDataJson = JSON.stringify({
233
+ types: {
234
+ EIP712Domain: [
235
+ { name: 'name', type: 'string' },
236
+ { name: 'version', type: 'string' },
237
+ { name: 'chainId', type: 'uint256' },
238
+ ],
239
+ Transfer: [
240
+ { name: 'to', type: 'address' },
241
+ { name: 'amount', type: 'uint256' },
242
+ ],
243
+ },
244
+ primaryType: 'Transfer',
245
+ domain: { name: 'MyToken', version: '1', chainId: 1 },
246
+ message: { to: '0xd8dA...', amount: '1000000' },
247
+ });
248
+
249
+ const parsed = parser.parse(typedDataJson);
250
+ console.log(parsed.primary_type); // "Transfer" (snake_case from Rust serde)
251
+
252
+ const domainHash = parser.domainSeparator(typedDataJson);
253
+ console.log(domainHash); // "0x..."
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Using CommonJS
259
+
260
+ ```javascript
261
+ const { EvmDecoder, MemoryRegistry, EvmCallDecoder, EvmEncoder, Eip712Parser } = require('@chainfoundry/chaincodec');
262
+
263
+ const registry = new MemoryRegistry();
264
+ registry.loadCsdl(csdlString);
265
+
266
+ const decoder = new EvmDecoder();
267
+ const event = decoder.decodeEvent(rawLog, registry);
268
+ ```
269
+
270
+ ---
271
+
272
+ ## API reference
273
+
274
+ ### `MemoryRegistry`
275
+
276
+ | Method / Property | Signature | Description |
277
+ | --- | --- | --- |
278
+ | `constructor` | `new MemoryRegistry()` | Create empty registry |
279
+ | `loadCsdl` | `(csdl: string) => number` | Load schemas from CSDL YAML string; returns count loaded |
280
+ | `loadFile` | `(path: string) => number` | Load a single `.csdl` file |
281
+ | `loadDirectory` | `(path: string) => number` | Load all `.csdl` files in a directory |
282
+ | `schemaCount` | `readonly number` | Number of schemas registered |
283
+ | `schemaNames` | `() => string[]` | List all schema names |
284
+
285
+ ### `EvmDecoder`
286
+
287
+ | Method | Signature | Description |
288
+ | --- | --- | --- |
289
+ | `constructor` | `new EvmDecoder()` | Create decoder |
290
+ | `decodeEvent` | `(raw: RawEvent, registry: MemoryRegistry) => DecodedEvent` | Decode a single EVM log |
291
+ | `decodeBatch` | `(raws: RawEvent[], registry: MemoryRegistry) => BatchDecodeResult` | Parallel-decode many logs |
292
+ | `fingerprint` | `(raw: RawEvent) => string` | Get topic0 fingerprint hex string |
293
+
294
+ ### `EvmCallDecoder`
295
+
296
+ | Method | Signature | Description |
297
+ | --- | --- | --- |
298
+ | `fromAbiJson` | `(abiJson: string) => EvmCallDecoder` | Create from ABI JSON (static factory) |
299
+ | `decodeCall` | `(calldata: string, functionName?: string or null) => DecodedCall` | Decode calldata |
300
+ | `functionNames` | `() => string[]` | List all function names in ABI |
301
+ | `selectorFor` | `(functionName: string) => string or null` | Get 4-byte selector hex |
302
+
303
+ ### `EvmEncoder`
304
+
305
+ | Method | Signature | Description |
306
+ | --- | --- | --- |
307
+ | `fromAbiJson` | `(abiJson: string) => EvmEncoder` | Create from ABI JSON (static factory) |
308
+ | `encodeCall` | `(functionName: string, argsJson: string) => string` | Encode; `argsJson` = `JSON.stringify(NormalizedValue[])`, returns `0x`-hex |
309
+
310
+ ### `Eip712Parser`
311
+
312
+ | Method | Signature | Description |
313
+ | --- | --- | --- |
314
+ | `constructor` | `new Eip712Parser()` | Create parser |
315
+ | `parse` | `(json: string) => TypedData` | Parse EIP-712 typed data JSON |
316
+ | `domainSeparator` | `(json: string) => string` | Compute domain separator hash |
317
+
318
+ ---
319
+
320
+ ## TypeScript types
321
+
322
+ ```typescript
323
+ interface RawEvent {
324
+ chain: string; // "ethereum" | "arbitrum" | "base" | "polygon" | "optimism" | numeric id
325
+ txHash: string;
326
+ blockNumber: number;
327
+ blockTimestamp: number; // Unix seconds
328
+ logIndex: number;
329
+ address: string; // contract address (hex, lowercase ok)
330
+ topics: string[]; // topics[0] = event signature hash
331
+ data: string; // hex with 0x prefix
332
+ }
333
+
334
+ interface DecodedEvent {
335
+ schema: string; // schema name, e.g. "ERC20Transfer"
336
+ schemaVersion: number;
337
+ chain: string;
338
+ txHash: string;
339
+ blockNumber: number;
340
+ blockTimestamp: number;
341
+ logIndex: number;
342
+ address: string;
343
+ fields: Record<string, NormalizedValue>; // decoded fields by name
344
+ fingerprint: string; // keccak256 of topics[0]
345
+ decodeErrors: Record<string, string>; // fields that failed to decode
346
+ }
347
+
348
+ interface DecodedCall {
349
+ functionName: string;
350
+ selector: string | null; // "0xaabbccdd"
351
+ inputs: Array<[string, NormalizedValue]>; // [name, value] pairs
352
+ decodeErrors: Record<string, string>;
353
+ }
354
+
355
+ type NormalizedValue =
356
+ | { type: 'uint'; value: number }
357
+ | { type: 'biguint'; value: string } // large uint256 as decimal string
358
+ | { type: 'int'; value: number }
359
+ | { type: 'bigint'; value: string }
360
+ | { type: 'bool'; value: boolean }
361
+ | { type: 'bytes'; value: number[] }
362
+ | { type: 'str'; value: string }
363
+ | { type: 'address'; value: string } // "0x..." lowercase
364
+ | { type: 'hash256'; value: string }
365
+ | { type: 'timestamp'; value: number }
366
+ | { type: 'array'; value: NormalizedValue[] }
367
+ | { type: 'tuple'; value: Array<[string, NormalizedValue]> }
368
+ | { type: 'null' }
369
+
370
+ interface BatchDecodeResult {
371
+ events: DecodedEvent[];
372
+ errors: Array<{ index: number; error: string }>;
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ## License
379
+
380
+ MIT — see [LICENSE](https://github.com/DarshanKumar89/chainkit/blob/main/LICENSE)
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chainfoundry/chaincodec",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Universal blockchain ABI decoder — production-grade event & call decoding for Ethereum and EVM chains",
5
5
  "keywords": [
6
6
  "ethereum",
@@ -12,7 +12,17 @@
12
12
  "chaincodec",
13
13
  "arbitrum",
14
14
  "base",
15
- "polygon"
15
+ "polygon",
16
+ "optimism",
17
+ "solidity",
18
+ "defi",
19
+ "uniswap",
20
+ "napi",
21
+ "rust-napi",
22
+ "native-addon",
23
+ "eth-logs",
24
+ "event-decoder",
25
+ "call-decoder"
16
26
  ],
17
27
  "license": "MIT",
18
28
  "repository": {
@@ -36,7 +46,8 @@
36
46
  "files": [
37
47
  "index.d.ts",
38
48
  "index.js",
39
- "chaincodec.*.node"
49
+ "chaincodec.*.node",
50
+ "README.md"
40
51
  ],
41
52
  "scripts": {
42
53
  "build": "napi build --platform --release",