chunktech 0.1.0 → 0.3.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 CHANGED
@@ -2,15 +2,17 @@
2
2
 
3
3
  On-chain file storage via chunked transactions for EVM chains.
4
4
 
5
- Store and retrieve files on Ethereum, Base, and Arbitrum using transaction calldata.
5
+ Store and retrieve files on Ethereum, Base, and Arbitrum using transaction calldata. Includes specialized support for censorship-resistant browser extension distribution.
6
6
 
7
7
  ## Features
8
8
 
9
9
  - **Chunk** - Split files into 33.3KB pieces for on-chain storage
10
- - **Send** - Broadcast chunks as transactions
10
+ - **Send** - Broadcast chunks as self-transfer transactions
11
11
  - **Track** - Monitor transaction confirmations
12
12
  - **Reassemble** - Reconstruct files from on-chain data
13
13
  - **Encrypt** - Optional X3DH encryption for private data
14
+ - **Cross-Chain** - Store data on L2, pointer on mainnet
15
+ - **Extensions** - Distribute Chrome/Firefox extensions via inscriptions
14
16
 
15
17
  ## Installation
16
18
 
@@ -23,27 +25,6 @@ For encryption support:
23
25
  npm install @noble/curves @noble/hashes
24
26
  ```
25
27
 
26
- ## Configuration
27
-
28
- Copy `.env.example` to `.env` and configure:
29
-
30
- ```bash
31
- cp .env.example .env
32
- ```
33
-
34
- ```env
35
- # Private key for signing transactions (without 0x prefix)
36
- PRIVATE_KEY=your_private_key_here
37
-
38
- # RPC URLs (optional - uses public RPCs if not set)
39
- RPC_BASE=https://mainnet.base.org
40
- RPC_ARBITRUM=https://arb1.arbitrum.io/rpc
41
- RPC_ETHEREUM=https://eth.llamarpc.com
42
-
43
- # Default chain
44
- DEFAULT_CHAIN=base
45
- ```
46
-
47
28
  ## Quick Start
48
29
 
49
30
  ```typescript
@@ -52,120 +33,220 @@ import { createWalletClient, http } from 'viem';
52
33
  import { base } from 'viem/chains';
53
34
  import { privateKeyToAccount } from 'viem/accounts';
54
35
 
55
- // Load from environment
56
36
  const account = privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`);
57
37
  const walletClient = createWalletClient({
58
38
  account,
59
39
  chain: base,
60
- transport: http(process.env.RPC_BASE),
40
+ transport: http(),
61
41
  });
62
42
 
63
- // Initialize ChunkTech
64
43
  const ct = new ChunkTech({ walletClient });
65
44
 
66
- // Upload a file
67
- const file = new Uint8Array([...]); // Your file data
68
- const result = await ct.upload(file, {
69
- onProgress: (sent, total) => console.log(`${sent}/${total} chunks sent`),
45
+ // Upload
46
+ const result = await ct.upload(fileData, {
47
+ onProgress: (sent, total) => console.log(`${sent}/${total}`),
70
48
  });
71
49
 
72
- console.log('Chunk ID:', result.chunkId);
73
- console.log('TX Hashes:', result.txHashes);
74
-
75
- // Download the file
50
+ // Download
76
51
  const downloaded = await ct.download(result.txHashes);
77
- console.log('Retrieved:', downloaded.data);
78
52
  ```
79
53
 
80
- ## API
54
+ ## Browser Extension Distribution
81
55
 
82
- ### `new ChunkTech(config)`
56
+ Distribute Chrome and Firefox extensions as on-chain inscriptions. The inscription itself is the installer - a self-contained HTML page that fetches extension data from L2 and offers downloads.
83
57
 
84
- Create a new ChunkTech instance.
58
+ ```
59
+ ┌─────────────────────────────────────────────────────────┐
60
+ │ Inscription (Ethereum) │
61
+ │ │
62
+ │ Self-contained HTML that: │
63
+ │ ├── Shows extension info + download buttons │
64
+ │ ├── Fetches chunks from Base via RPC │
65
+ │ ├── Reassembles + verifies SHA256 │
66
+ │ └── Downloads as .zip (Chrome) or .xpi (Firefox) │
67
+ │ │
68
+ └─────────────────────────────────────────────────────────┘
69
+ ```
70
+
71
+ ### Upload Extension
85
72
 
86
73
  ```typescript
87
- interface ChunkTechConfig {
88
- walletClient: WalletClient; // viem WalletClient for signing
89
- publicClient?: PublicClient; // Optional, created from walletClient if not provided
90
- chain?: Chain; // Optional, detected from walletClient
91
- rpcUrl?: string; // Optional custom RPC URL
92
- }
74
+ import { ExtensionUploader } from 'chunktech';
75
+ import { createWalletClient, http } from 'viem';
76
+ import { base, mainnet } from 'viem/chains';
77
+ import { privateKeyToAccount } from 'viem/accounts';
78
+ import { readFileSync } from 'fs';
79
+
80
+ const account = privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`);
81
+
82
+ const uploader = new ExtensionUploader({
83
+ keyChain: 'ethereum', // Inscription lives here (1 tx)
84
+ dataChain: 'base', // Extension data lives here (cheap)
85
+ keyWalletClient: createWalletClient({
86
+ account,
87
+ chain: mainnet,
88
+ transport: http(),
89
+ }),
90
+ dataWalletClient: createWalletClient({
91
+ account,
92
+ chain: base,
93
+ transport: http(),
94
+ }),
95
+ });
96
+
97
+ const result = await uploader.upload({
98
+ name: 'My Extension',
99
+ version: '1.0.0',
100
+ developer: 'vitalik.eth',
101
+ description: 'A censorship-resistant browser extension',
102
+ homepage: 'https://myextension.xyz',
103
+ chrome: readFileSync('dist/chrome.zip'),
104
+ firefox: readFileSync('dist/firefox.xpi'),
105
+ }, {
106
+ onProgress: (phase, sent, total) => {
107
+ console.log(`${phase}: ${sent}/${total}`);
108
+ },
109
+ });
110
+
111
+ console.log('Inscription TX:', result.inscriptionTxHash);
112
+ // View at: https://ethscriptions.com/ethscriptions/0x...
113
+ ```
114
+
115
+ ### What Users See
116
+
117
+ The inscription renders as a download page:
118
+
93
119
  ```
120
+ ╔═══════════════════════════════════════════════════╗
121
+ ║ My Extension v1.0.0 ║
122
+ ║ vitalik.eth ║
123
+ ╚═══════════════════════════════════════════════════╝
124
+
125
+ A censorship-resistant browser extension
126
+
127
+ ┌─────────────────┐ ┌─────────────────┐
128
+ │ Chrome/Brave │ │ Firefox │
129
+ │ [Detected] │ │ │
130
+ │ [Download] │ │ [Download] │
131
+ │ 142 KB │ │ 138 KB │
132
+ └─────────────────┘ └─────────────────┘
133
+
134
+ Installation Instructions:
135
+ 1. Download the extension
136
+ 2. Unzip (Chrome) or keep as .xpi (Firefox)
137
+ 3. Load in developer mode / Install from file
138
+ ```
139
+
140
+ ### Why This Matters
141
+
142
+ - **Censorship-resistant** - No app store can remove it
143
+ - **Immutable** - Code is permanently on-chain
144
+ - **Verifiable** - SHA256 verified on download
145
+ - **Self-contained** - The inscription IS the installer
146
+ - **Multi-browser** - Chrome, Brave, Edge, Firefox from one inscription
94
147
 
95
- ### `upload(data, options)`
148
+ ## Cross-Chain Upload
96
149
 
97
- Upload data to the blockchain.
150
+ Store bulk data on L2 (cheap), pointer on mainnet (durable):
98
151
 
99
152
  ```typescript
100
- const result = await ct.upload(data, {
101
- encrypt: false, // Enable encryption
102
- keys: encryptionKeys, // Required if encrypt: true
103
- recipients: [...], // Recipients for encrypted data
104
- onProgress: (sent, total) => {}, // Progress callback
105
- confirmations: 1, // Wait for confirmations
153
+ import { CrossChainUploader } from 'chunktech';
154
+
155
+ const uploader = new CrossChainUploader({
156
+ keyChain: 'ethereum',
157
+ dataChain: 'base',
158
+ keyWalletClient,
159
+ dataWalletClient,
160
+ });
161
+
162
+ const result = await uploader.upload(fileData, {
163
+ format: 'html',
164
+ title: 'My File',
106
165
  });
107
166
 
108
- // Returns:
109
- {
110
- chunkId: string; // Unique ID for this upload
111
- txHashes: Hash[]; // Transaction hashes
112
- totalChunks: number; // Number of chunks sent
113
- totalBytes: number; // Original data size
114
- }
167
+ // result.keyTxHash = mainnet inscription (self-loading HTML)
168
+ // result.dataTxHashes = Base data chunks
115
169
  ```
116
170
 
117
- ### `download(txHashes, options)`
171
+ ## API Reference
172
+
173
+ ### ChunkTech
118
174
 
119
- Download and reassemble data from the blockchain.
175
+ Main class for single-chain uploads.
120
176
 
121
177
  ```typescript
122
- const result = await ct.download(txHashes, {
123
- keys: encryptionKeys, // Required if data was encrypted
124
- recipientId: 'myId', // Required if data was encrypted
178
+ const ct = new ChunkTech({
179
+ walletClient: WalletClient, // viem wallet
180
+ publicClient?: PublicClient, // Optional
181
+ chain?: Chain, // Auto-detected
182
+ rpcUrl?: string, // Custom RPC
125
183
  });
126
184
 
127
- // Returns:
128
- {
129
- data: Uint8Array; // The reassembled file
130
- chunkId: string; // Chunk set ID
131
- totalChunks: number; // Number of chunks retrieved
132
- }
185
+ // Upload
186
+ const result = await ct.upload(data, {
187
+ encrypt?: boolean,
188
+ keys?: EncryptionKeys,
189
+ recipients?: Recipient[],
190
+ onProgress?: (sent, total) => void,
191
+ confirmations?: number,
192
+ });
193
+
194
+ // Download
195
+ const result = await ct.download(txHashes, {
196
+ keys?: EncryptionKeys,
197
+ recipientId?: string,
198
+ });
133
199
  ```
134
200
 
135
- ## Encryption
201
+ ### ExtensionUploader
136
202
 
137
- ChunkTech supports optional X3DH encryption for private data.
203
+ Specialized uploader for browser extensions.
138
204
 
139
205
  ```typescript
140
- import { generateEncryptionKeys, deriveKeysFromSignature } from 'chunktech';
206
+ const uploader = new ExtensionUploader({
207
+ keyChain: 'ethereum' | 'sepolia',
208
+ dataChain: 'base' | 'baseSepolia',
209
+ keyWalletClient: WalletClient,
210
+ dataWalletClient: WalletClient,
211
+ keyRpcUrl?: string,
212
+ dataRpcUrl?: string,
213
+ });
141
214
 
142
- // Generate new keys
143
- const myKeys = await generateEncryptionKeys();
215
+ const result = await uploader.upload({
216
+ name: string,
217
+ version: string,
218
+ developer: string,
219
+ description?: string,
220
+ homepage?: string,
221
+ chrome?: Uint8Array,
222
+ firefox?: Uint8Array,
223
+ }, {
224
+ onProgress?: (phase, sent, total) => void,
225
+ confirmations?: number,
226
+ });
227
+ ```
144
228
 
145
- // Or derive from wallet signature (deterministic)
146
- const signature = await wallet.signMessage('chunktech-keys');
147
- const myKeys = await deriveKeysFromSignature(signature);
229
+ ### CrossChainUploader
148
230
 
149
- // Upload encrypted data
150
- await ct.upload(data, {
151
- encrypt: true,
152
- keys: myKeys,
153
- recipients: [
154
- { id: 'alice', bundle: aliceBundle },
155
- { id: 'bob', bundle: bobBundle },
156
- ],
231
+ General-purpose cross-chain uploader with HTML loader.
232
+
233
+ ```typescript
234
+ const uploader = new CrossChainUploader({
235
+ keyChain: ChainName,
236
+ dataChain: ChainName,
237
+ keyWalletClient: WalletClient,
238
+ dataWalletClient: WalletClient,
157
239
  });
158
240
 
159
- // Download encrypted data
160
- const result = await ct.download(txHashes, {
161
- keys: myKeys,
162
- recipientId: 'sender', // Use 'sender' if you're the uploader
241
+ const result = await uploader.upload(data, {
242
+ format?: 'html' | 'json',
243
+ title?: string,
244
+ description?: string,
245
+ mimeType?: string,
163
246
  });
164
247
  ```
165
248
 
166
- ## Low-Level API
167
-
168
- For more control, use the individual modules:
249
+ ### Low-Level Utilities
169
250
 
170
251
  ```typescript
171
252
  import {
@@ -175,75 +256,151 @@ import {
175
256
  decodeChunk,
176
257
  reassembleChunks,
177
258
  ChunkTracker,
259
+ estimateChunks,
178
260
 
179
261
  // Sending
180
262
  sendChunk,
181
263
  sendChunks,
264
+ sendChunksParallel,
182
265
 
183
266
  // Tracking
267
+ waitForTransaction,
184
268
  waitForTransactions,
185
269
  TransactionMonitor,
186
270
 
187
271
  // Fetching
188
272
  fetchChunk,
273
+ fetchChunks,
189
274
  assembleFromHashes,
190
275
  StreamingAssembler,
276
+
277
+ // Encryption
278
+ generateEncryptionKeys,
279
+ deriveKeysFromSignature,
280
+ encryptForRecipients,
281
+ decryptForRecipient,
191
282
  } from 'chunktech';
192
283
  ```
193
284
 
194
- ### Chunking
285
+ ## Encryption
286
+
287
+ Optional X3DH + AES-256-GCM encryption for private data.
195
288
 
196
289
  ```typescript
197
- // Split data into chunks
198
- const chunked = chunkData(myData);
199
- console.log(`${chunked.total} chunks, ID: ${chunked.id}`);
290
+ import { generateEncryptionKeys } from 'chunktech';
200
291
 
201
- // Encode for on-chain storage
202
- const calldata = encodeChunk(chunked.chunks[0]);
292
+ const myKeys = await generateEncryptionKeys();
203
293
 
204
- // Decode from calldata
205
- const chunk = decodeChunk(calldata);
294
+ // Upload encrypted
295
+ await ct.upload(data, {
296
+ encrypt: true,
297
+ keys: myKeys,
298
+ recipients: [
299
+ { id: 'alice', bundle: aliceKeys.bundle },
300
+ ],
301
+ });
302
+
303
+ // Download encrypted
304
+ const result = await ct.download(txHashes, {
305
+ keys: myKeys,
306
+ recipientId: 'sender',
307
+ });
206
308
  ```
207
309
 
208
- ### Streaming Reassembly
310
+ ## Supported Chains
209
311
 
210
- ```typescript
211
- const tracker = new ChunkTracker();
312
+ | Chain | ID | Use Case |
313
+ |-------|-----|----------|
314
+ | Ethereum | 1 | Inscriptions, durability |
315
+ | Base | 8453 | Cheap data storage |
316
+ | Arbitrum | 42161 | Cheap data storage |
317
+ | Sepolia | 11155111 | Testing |
318
+ | Base Sepolia | 84532 | Testing |
319
+ | Arbitrum Sepolia | 421614 | Testing |
320
+
321
+ ## Cost Estimates
212
322
 
213
- for (const txHash of incomingHashes) {
214
- const result = await fetchChunk(publicClient, txHash);
215
- if (result.chunk) {
216
- const assembled = tracker.add(result.chunk);
217
- if (assembled) {
218
- console.log('Complete!', assembled);
219
- }
220
- }
221
- }
323
+ For a 500KB extension on Base:
222
324
 
223
- // Check progress
224
- console.log(tracker.getIncomplete());
225
- // [{ id: '...', have: 2, need: 5 }]
226
325
  ```
326
+ Chunks: ~15 (at 33KB each)
327
+ Cost per chunk: ~$0.002
328
+ Total: ~$0.03
227
329
 
228
- ## Supported Chains
330
+ + 1 Ethereum inscription: ~$2-5 (varies with gas)
331
+ ```
229
332
 
230
- | Chain | Chain ID |
231
- |-------|----------|
232
- | Ethereum | 1 |
233
- | Base | 8453 |
234
- | Arbitrum | 42161 |
235
- | Sepolia | 11155111 |
236
- | Base Sepolia | 84532 |
237
- | Arbitrum Sepolia | 421614 |
333
+ ## How It Works
238
334
 
239
- ## Constants
335
+ 1. **Chunking** - Files split into 33.3KB pieces with metadata (ID, part, total)
336
+ 2. **Encoding** - Each chunk → JSON → base64 → prefixed calldata
337
+ 3. **Sending** - Self-transfer transactions (to == from, value = 0)
338
+ 4. **Storage** - Calldata stored permanently in transaction history
339
+ 5. **Retrieval** - Fetch via `eth_getTransactionByHash`, decode, reassemble
340
+ 6. **Verification** - SHA256 hash checked after reassembly
240
341
 
241
- ```typescript
242
- import { MAX_CHUNK_SIZE } from 'chunktech';
342
+ ## On-Chain Viewer Pages
343
+
344
+ Create standalone HTML pages that fetch, verify, and display on-chain content. Perfect for:
345
+ - NPM packages with auditable source
346
+ - Browser extensions with install instructions
347
+ - Any file needing public verification
348
+
349
+ ### Quick Inscribe
350
+
351
+ ```javascript
352
+ import { readFileSync } from 'fs';
353
+ import { createWalletClient, http, toHex } from 'viem';
354
+ import { base } from 'viem/chains';
355
+ import { createHash } from 'crypto';
356
+
357
+ const zipData = readFileSync('package.zip');
358
+ const base64 = zipData.toString('base64');
359
+ const sha256 = createHash('sha256').update(zipData).digest('hex');
360
+
361
+ const dataUri = `data:application/zip;base64,${base64}`;
362
+ const calldata = toHex(new TextEncoder().encode(dataUri));
363
+
364
+ const hash = await walletClient.sendTransaction({
365
+ to: account.address,
366
+ data: calldata,
367
+ });
368
+
369
+ console.log(`TX: ${hash}`);
370
+ console.log(`SHA256: ${sha256}`);
371
+ ```
372
+
373
+ ### Viewer HTML
374
+
375
+ The viewer fetches from any RPC, verifies SHA256, and displays source:
243
376
 
244
- // MAX_CHUNK_SIZE = 33333 (33.3KB)
245
- // This ensures wrapped chunks stay under 128KB tx limit
246
377
  ```
378
+ ┌─────────────────────────────────────────┐
379
+ │ MyPackage v1.0.0 │
380
+ │ │
381
+ │ [Download] [Verify On-Chain] │
382
+ │ │
383
+ │ ┌─────────────────────────────────┐ │
384
+ │ │ README │ index.ts │ package.json│ │
385
+ │ ├─────────────────────────────────┤ │
386
+ │ │ │ │
387
+ │ │ // Source code displayed here │ │
388
+ │ │ │ │
389
+ │ └─────────────────────────────────┘ │
390
+ └─────────────────────────────────────────┘
391
+ ```
392
+
393
+ Key features:
394
+ - Fetches tx via public RPC (no backend)
395
+ - SHA256 verification on download
396
+ - Source code tabs for auditing
397
+ - Works offline once loaded
398
+
399
+ See `skill.md` for full implementation details.
400
+
401
+ ## Acknowledgments
402
+
403
+ - [calldata-rpc](https://github.com/chopperdaddy/calldata-rpc) by [@chopperdaddy](https://github.com/chopperdaddy) - Decentralized RPC endpoint discovery via IPFS and ENS, used in the HTML loaders for reliable chain access.
247
404
 
248
405
  ## License
249
406
 
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ChunkTech - Cross-Chain Upload
3
+ * Key transaction on mainnet, bulk data on L2
4
+ */
5
+ import type { CrossChainConfig, CrossChainUploadOptions, CrossChainUploadResult, CrossChainManifest } from './types.js';
6
+ export declare class CrossChainUploader {
7
+ private keyWalletClient;
8
+ private dataWalletClient;
9
+ private keyPublicClient;
10
+ private dataPublicClient;
11
+ private keyChain;
12
+ private dataChain;
13
+ constructor(config: CrossChainConfig);
14
+ /**
15
+ * Upload data across chains - key on mainnet, chunks on L2
16
+ */
17
+ upload(data: Uint8Array | ArrayBuffer | string, options?: CrossChainUploadOptions): Promise<CrossChainUploadResult>;
18
+ }
19
+ declare function generateHtmlLoader(manifest: CrossChainManifest, options: {
20
+ title: string;
21
+ description: string;
22
+ }): string;
23
+ export { generateHtmlLoader };
24
+ //# sourceMappingURL=crosschain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crosschain.d.ts","sourceRoot":"","sources":["../src/crosschain.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAEV,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,EAEnB,MAAM,YAAY,CAAC;AAuBpB,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,gBAAgB,CAAe;IAEvC,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,SAAS,CAAY;gBAEjB,MAAM,EAAE,gBAAgB;IAiBpC;;OAEG;IACG,MAAM,CACV,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,MAAM,EACvC,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC;CAwFnC;AAMD,iBAAS,kBAAkB,CACzB,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC9C,MAAM,CA+IR;AA0ED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}