@healchain/sdk 0.5.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +58 -109
  2. package/package.json +1 -1
  3. package/src/index.js +76 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @healchain/sdk
2
2
 
3
- JavaScript SDK for [HealChain](https://healchain.org) — self-healing decentralized storage using Reed-Solomon erasure coding on Ethereum and Arbitrum.
3
+ JavaScript SDK for [HealChain](https://healchain.org) — self-healing decentralized storage using Reed-Solomon erasure coding across multiple EVM chains.
4
4
 
5
5
  ## Install
6
6
 
@@ -13,7 +13,10 @@ npm install @healchain/sdk
13
13
  ```javascript
14
14
  import HealChain from '@healchain/sdk';
15
15
 
16
- const hc = new HealChain({ apiUrl: 'https://api.healchain.org' });
16
+ const hc = new HealChain({
17
+ apiUrl: 'https://api.healchain.org',
18
+ apiKey: 'your-api-key',
19
+ });
17
20
 
18
21
  // Store data — oracle fulfills automatically
19
22
  const { recordId, chainId } = await hc.store('Hello World', {
@@ -33,8 +36,9 @@ console.log(`Retrieved from chain ${retrievedFrom}: ${text}`);
33
36
  | Option | Type | Default | Description |
34
37
  |---|---|---|---|
35
38
  | `apiUrl` | string | `https://api.healchain.org` | API base URL |
36
- | `apiKey` | string | — | Optional API key |
37
- | `network` | string | `'sepolia'` | Default network: `'sepolia'` or `'arbitrum'` |
39
+ | `apiKey` | string | — | API key (required for store/retrieve) |
40
+ | `chain` | string | `'auto'` | Chain: `'auto'`, `'sepolia'`, `'arbitrum'`, `'base'`, etc. |
41
+ | `preference` | string | `''` | `'cheapest'` \| `'fastest'` \| `'redundant'` \| `''` (auto weighted) |
38
42
  | `pollInterval` | number | `4000` | Oracle poll interval in ms |
39
43
  | `pollTimeout` | number | `120000` | Max wait for oracle fulfillment in ms |
40
44
  | `dataShards` | number | `10` | Default RS data shards |
@@ -49,156 +53,101 @@ Store data on-chain. Polls until the oracle fulfills the request.
49
53
  **Parameters:**
50
54
  - `data` — `string` or `Uint8Array`
51
55
  - `options.label` — record label (default: `'sdk upload'`)
52
- - `options.network` — override network for this call
53
- - `options.dataShards` / `options.parityShards` — override shard config
54
- - `options.onPending(info)` — callback when tx is submitted
55
- - `options.onFulfilled(result)` — callback when oracle fulfills
56
-
57
- **Returns:** `Promise<StoreResult>`
58
-
59
- ```typescript
60
- {
61
- recordId: string // on-chain record ID
62
- requestId: string // oracle request ID
63
- tx: string // submission transaction hash
64
- chainId: string // chain where data is stored
65
- originalSize: string // original data size in bytes
66
- encodedSize: string // encoded shard size in bytes
67
- }
68
- ```
56
+ - `options.chain` — override chain for this store
57
+ - `options.preference` — override preference for this store
58
+ - `options.onFulfilled` — callback when oracle fulfills: `(result) => void`
69
59
 
70
- **Example with progress callbacks:**
71
-
72
- ```javascript
73
- const result = await hc.store('my data', {
74
- label: 'important file',
75
- network: 'arbitrum', // ~75x cheaper than Sepolia
76
- onPending: ({ requestId }) => {
77
- console.log(`Submitted, oracle processing requestId=${requestId}...`);
78
- },
79
- onFulfilled: ({ recordId }) => {
80
- console.log(`Fulfilled! Record ID: ${recordId}`);
81
- },
82
- });
83
- ```
60
+ **Returns:** `{ recordId, tx, chainId, originalSize, encodedSize, ciDigestHash }`
84
61
 
85
62
  ---
86
63
 
87
64
  ### `hc.retrieve(id)`
88
65
 
89
- Retrieve a record by ID. Automatically queries the GlobalRegistry to find which chain holds the data and fetches from there.
66
+ Retrieve data by record ID. Auto-discovers which chain the record lives on.
90
67
 
91
- **Parameters:**
92
- - `id` — `string` or `number`
68
+ **Returns:** `{ text, hex, chainId, originalSize, label }`
93
69
 
94
- **Returns:** `Promise<RetrieveResult>`
70
+ ---
95
71
 
96
- ```typescript
97
- {
98
- recordId: string // record ID
99
- data: string // hex-encoded original data (0x...)
100
- text: string // UTF-8 decoded text
101
- bytes: number // original data size in bytes
102
- chainId: string // chain where data was stored ('11155111' or '421614')
103
- }
72
+ ### `hc.estimateFee(sizeBytes, chain?)`
73
+
74
+ Estimate the fee for storing data of a given size.
75
+
76
+ **Returns:** `{ feeCents, feeUsd, sizeBytes, chain }`
77
+
78
+ ```javascript
79
+ const fee = await hc.estimateFee(1024, 'auto');
80
+ console.log(`Fee: ${fee.feeUsd}`); // e.g. "$0.05"
104
81
  ```
105
82
 
106
83
  ---
107
84
 
85
+ ### `hc.chainScores()`
86
+
87
+ Get current chain scoring data (cost, reliability, latency).
88
+
89
+ **Returns:** array of chain score objects
90
+
91
+ ---
92
+
108
93
  ### `hc.getMetadata(id)`
109
94
 
110
- Fetch record metadata without retrieving the full data payload.
95
+ Get record metadata without retrieving the data.
111
96
 
112
- **Returns:** `Promise<object>` label, owner, sizes, shards, timestamp, dataHash
97
+ **Returns:** `{ id, label, originalSize, encodedSize, dataShards, parityShards, owner, timestamp, chainId }`
113
98
 
114
99
  ---
115
100
 
116
101
  ### `hc.list(page?, limit?)`
117
102
 
118
- List records with pagination.
103
+ List stored records.
119
104
 
120
- **Parameters:**
121
- - `page` — page number, 0-indexed (default: `0`)
122
- - `limit` — records per page, max 50 (default: `10`)
123
-
124
- **Returns:**
125
- ```typescript
126
- {
127
- records: object[] // record summaries
128
- total: number // total record count
129
- pages: number // total page count
130
- page: number // current page
131
- }
132
- ```
105
+ **Returns:** `{ records, total, pages }`
106
+
107
+ ---
108
+
109
+ ### `hc.getBillingBalance(walletAddress)`
110
+
111
+ Get testnet credit balance for a wallet.
133
112
 
134
113
  ---
135
114
 
136
115
  ### `hc.health()`
137
116
 
138
- Check service health.
117
+ Check API health.
139
118
 
140
- **Returns:** `Promise<{ status, version, geth, lastBlock }>`
119
+ **Returns:** `{ status, version, lastBlock }`
141
120
 
142
121
  ---
143
122
 
144
123
  ## Error Handling
145
124
 
146
- All methods throw `HealChainError` on failure:
147
-
148
125
  ```javascript
149
- import { HealChain, HealChainError } from '@healchain/sdk';
126
+ import HealChain, { HealChainError } from '@healchain/sdk';
150
127
 
151
128
  try {
152
- const result = await hc.retrieve(999);
129
+ const result = await hc.store('my data', { label: 'test' });
153
130
  } catch (err) {
154
131
  if (err instanceof HealChainError) {
155
- console.error(err.message); // human-readable message
156
- console.error(err.status); // HTTP status code (if applicable)
157
- console.error(err.code); // 'NETWORK_ERROR' | 'FULFILLMENT_TIMEOUT' | null
132
+ console.error(err.message, err.status, err.code);
158
133
  }
159
134
  }
160
135
  ```
161
136
 
162
- ---
163
-
164
- ## Browser (ESM)
165
-
166
- ```html
167
- <script type="module">
168
- import HealChain from 'https://esm.sh/@healchain/sdk';
169
-
170
- const hc = new HealChain({ apiUrl: 'https://api.healchain.org' });
171
- const { text } = await hc.retrieve(0);
172
- console.log(text);
173
- </script>
174
- ```
175
-
176
- ---
177
-
178
- ## Networks
137
+ ## Supported Chains
179
138
 
180
- | Network | Chain ID | Notes |
139
+ | Chain | ID | Identifier |
181
140
  |---|---|---|
182
- | `sepolia` | 11155111 | Ethereum Sepolia testnet |
183
- | `arbitrum` | 421614 | Arbitrum Sepolia testnet — ~75x cheaper gas |
184
-
185
- Store on Arbitrum for lower cost. Retrieve works automatically regardless of which chain the data is on — the SDK doesn't need to know.
186
-
187
- ---
188
-
189
- ## Roadmap
141
+ | Ethereum Sepolia | 11155111 | `sepolia` |
142
+ | Arbitrum Sepolia | 421614 | `arbitrum` |
143
+ | Base Sepolia | 84532 | `base` |
144
+ | Optimism Sepolia | 11155420 | `optimism` |
145
+ | Unichain Sepolia | 1301 | `unichain` |
146
+ | Avalanche Fuji | 43113 | `avalanche` |
147
+ | And 7 more testnets... | | `auto` selects best |
190
148
 
191
- - **v0.1** REST API client (store, retrieve, list, health)
192
- - **v0.2** — Direct wallet signing via ethers.js (no service required)
193
- - **v0.3** — Streaming large file support
194
- - **v1.0** — Mainnet support
195
-
196
- ---
149
+ Use `chain: 'auto'` (default) to let the scoring engine pick the optimal chain.
197
150
 
198
151
  ## License
199
152
 
200
- MIT see [LICENSE](LICENSE)
201
-
202
- ---
203
-
204
- *Built on [HealChain](https://healchain.org) · [GitHub](https://github.com/karmaxul/ci-quantum-storage)*
153
+ MIT © [HealChain](https://healchain.org)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@healchain/sdk",
3
- "version": "0.5.0",
3
+ "version": "1.1.0",
4
4
  "description": "JavaScript SDK for HealChain — self-healing decentralized storage via Reed-Solomon erasure coding",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HealChain SDK v0.4.0
2
+ * HealChain SDK v1.1.0
3
3
  * REST API client for HealChain — self-healing decentralized storage.
4
4
  *
5
5
  * Browser (ESM) and Node.js compatible.
@@ -76,7 +76,10 @@ class HealChain {
76
76
  this.dataShards = options.dataShards ?? DEFAULT_DATA_SHARDS;
77
77
  this.parityShards = options.parityShards ?? DEFAULT_PARITY_SHARDS;
78
78
  this.chain = options.chain ?? options.network ?? 'auto';
79
- this.network = this.chain; // backward compat
79
+ this.network = this.chain; // backward compat — use chain instead
80
+ if (options.network && !options.chain) {
81
+ console.warn('[HealChain] options.network is deprecated — use options.chain instead');
82
+ }
80
83
  this.preference = options.preference ?? '';
81
84
  }
82
85
 
@@ -238,14 +241,82 @@ class HealChain {
238
241
  * @property {number} bytes - Original data size in bytes
239
242
  * @property {string} chainId - Chain where data was stored
240
243
  */
241
- async retrieve(id) {
242
- const result = await this._fetch(`/retrieve?id=${encodeURIComponent(String(id))}`);
243
- return {
244
+ async retrieve(id, options = {}) {
245
+ const chain = options.chain ?? this.chain ?? 'auto';
246
+ const result = await this._fetch(
247
+ `/retrieve?id=${encodeURIComponent(String(id))}&chain=${encodeURIComponent(chain)}`
248
+ );
249
+ const record = {
244
250
  recordId: String(result.recordId),
245
251
  data: result.data,
246
252
  text: result.text,
247
253
  bytes: result.bytes,
248
254
  chainId: result.chainId ?? null,
255
+ storageType: result.storageType ?? 'on_chain',
256
+ shards: result.shards ?? null,
257
+ };
258
+ // Fetch full ci-sha4096 digest — best-effort, non-blocking
259
+ try {
260
+ record.ciDigestHash = await this.getCiDigest(id);
261
+ } catch (_) {
262
+ // digest not yet available for old records — silently omit
263
+ }
264
+ return record;
265
+ }
266
+
267
+ // ── storeFile() ────────────────────────────────────────────────────────────
268
+
269
+ /**
270
+ * Store a file (raw bytes) on-chain or via IPFS hybrid (auto-detected by size).
271
+ * Files ≤1MB go on-chain. Files >1MB use IPFS hybrid storage.
272
+ *
273
+ * @param {Uint8Array|ArrayBuffer|Buffer} buffer - File bytes
274
+ * @param {object} [options]
275
+ * @param {string} [options.label='sdk upload'] - Record label
276
+ * @param {number} [options.dataShards] - Override RS data shards
277
+ * @param {number} [options.parityShards] - Override RS parity shards
278
+ * @param {string} [options.chain] - Chain name or 'auto'
279
+ * @param {string} [options.preference] - Chain selection preference
280
+ * @returns {Promise<StoreResult>}
281
+ */
282
+ async storeFile(buffer, options = {}) {
283
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
284
+ return this.store(bytes, options);
285
+ }
286
+
287
+ // ── getCiDigest() ──────────────────────────────────────────────────────────
288
+
289
+ /**
290
+ * Get the full 512-byte ci-sha4096 digest hex for a record.
291
+ * This is the authoritative digest stored at oracle fulfillment time.
292
+ *
293
+ * @param {string|number} recordId - Record ID
294
+ * @returns {Promise<string>} Full digest hex (1024 hex chars = 512 bytes)
295
+ */
296
+ async getCiDigest(recordId) {
297
+ const result = await this._fetch(`/ciDigest?recordId=${encodeURIComponent(String(recordId))}`);
298
+ return result.ciDigestHash;
299
+ }
300
+
301
+ // ── verifyIntegrity() ──────────────────────────────────────────────────────
302
+
303
+ /**
304
+ * High-level audit helper: retrieve a record and its ci-sha4096 digest.
305
+ *
306
+ * @param {string|number} recordId - Record ID
307
+ * @param {object} [options]
308
+ * @param {string} [options.chain] - Chain name or 'auto'
309
+ * @returns {Promise<{record: RetrieveResult, ciDigestHash: string|null}>}
310
+ */
311
+ async verifyIntegrity(recordId, options = {}) {
312
+ const chain = options.chain ?? this.chain ?? 'auto';
313
+ const [record, ciDigestHash] = await Promise.allSettled([
314
+ this._fetch(`/retrieve?id=${encodeURIComponent(String(recordId))}&chain=${encodeURIComponent(chain)}`),
315
+ this.getCiDigest(recordId),
316
+ ]);
317
+ return {
318
+ record: record.status === 'fulfilled' ? record.value : null,
319
+ ciDigestHash: ciDigestHash.status === 'fulfilled' ? ciDigestHash.value : null,
249
320
  };
250
321
  }
251
322