gethstorage-sdk 5.0.2 → 5.0.3

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
@@ -1,208 +1,208 @@
1
- # gethstorage-sdk
2
-
3
- This SDK aims to standardize the interaction between applications and the EthStorage network to achieve reliable and
4
- efficient data management functionality.
5
-
6
- This SDK provides the `FlatDirectory` class, a higher-level data management tool that provides methods for uploading
7
- and downloading data of arbitrary size. Uploads are performed by sending the data through calldata.
8
-
9
- Click here to view [spec](./spec.md).
10
-
11
- # Installation
12
-
13
- Install the SDK using [npm](https://www.npmjs.com/package/gethstorage-sdk):
14
-
15
- ```bash
16
- $ npm install gethstorage-sdk
17
- ```
18
-
19
- # Example Usage
20
-
21
- ## FlatDirectory
22
-
23
- ### create
24
-
25
- Create a `FlatDirectory` instance.
26
-
27
- ```js
28
- const { FlatDirectory } = require("gethstorage-sdk");
29
-
30
- const rpc = "https://rpc.beta.testnet.l2.quarkchain.io:8545";
31
- const ethStorageRpc = "https://rpc.beta.testnet.l2.ethstorage.io:9596";
32
- const privateKey = "0xabcd...";
33
-
34
- const flatDirectory = await FlatDirectory.create({
35
- rpc: rpc,
36
- ethStorageRpc: ethStorageRpc,
37
- privateKey: privateKey,
38
- });
39
- ```
40
-
41
- If `FlatDirectory` has been deployed, it can be set through the `address` field.
42
-
43
- ```js
44
- const address = "0x987..."; // FlatDirectory address
45
- const flatDirectory = await FlatDirectory.create({
46
- rpc: rpc,
47
- ethStorageRpc: ethStorageRpc,
48
- privateKey: privateKey,
49
- address: address,
50
- });
51
- ```
52
-
53
- If you only need the `download` function, you can create a `FlatDirectory` using the following pattern.
54
-
55
- ```js
56
- const address = "0x987..."; // FlatDirectory address
57
- const flatDirectory = await FlatDirectory.create({
58
- ethStorageRpc: 'https://rpc.beta.testnet.l2.ethstorage.io:9596',
59
- address: address
60
- });
61
- ```
62
-
63
- ### deploy
64
-
65
- Deploy the implementation
66
- contract [FlatDirectory](https://github.com/ethstorage/evm-large-storage)
67
- for [EIP-5018](https://eips.ethereum.org/EIPS/eip-5018) standard.
68
-
69
- ```js
70
- const contractAddress = await flatDirectory.deploy();
71
- console.log(`FlatDirectory address is ${contractAddress}.`);
72
- ```
73
-
74
- ### upload
75
-
76
- Upload `buffer | file` to the FlatDirectory. The content is sliced into chunks and each chunk is sent through calldata.
77
-
78
- ```js
79
- const callback = {
80
- onProgress: function (progress, count, isChange) {
81
- console.log(`Uploaded ${progress} of ${count} chunks`);
82
- },
83
- onFail: function (err) {
84
- console.log(err);
85
- },
86
- onFinish: function (totalUploadChunks, totalUploadSize, totalCost) {
87
- console.log(`Total upload chunk count is ${totalUploadChunks}, size is ${totalUploadSize}, total cost is ${totalCost}`);
88
- }
89
- };
90
-
91
- const request = {
92
- key: "test.txt",
93
- content: Buffer.from("big data"),
94
- callback: callback
95
- }
96
- await flatDirectory.upload(request);
97
- ```
98
-
99
- If you want to use `file`, it can be divided into browser and Node.js.
100
-
101
- Browser
102
- ```javascript
103
- // <input id='fileToUpload' />
104
- const file = document.getElementById('fileToUpload').files[0];
105
-
106
- const request = {
107
- key: "test.txt",
108
- content: file,
109
- callback: callback
110
- }
111
- await flatDirectory.upload(request);
112
- ```
113
-
114
- Node.js
115
- ```javascript
116
- const {NodeFile} = require("gethstorage-sdk/file");
117
- const file = new NodeFile("/usr/download/test.jpg");
118
-
119
- const request = {
120
- key: "test.txt",
121
- content: file,
122
- callback: callback
123
- }
124
- await flatDirectory.upload(request);
125
- ```
126
-
127
- ### download
128
-
129
- Monitor the download progress by passing in a callback function.
130
-
131
- ```js
132
- const key = "test.txt";
133
- await flatDirectory.download(key, {
134
- onProgress: function (progress, count, chunk) {
135
- console.log(`Download ${progress} of ${count} chunks, this chunk is ${Buffer.from(chunk).toString()}`);
136
- },
137
- onFail: function (error) {
138
- console.error("Error download data:", error);
139
- },
140
- onFinish: function () {
141
- console.log("Download success.");
142
- }
143
- });
144
- ```
145
-
146
- ### estimateCost
147
-
148
- Estimate gas costs before uploading.
149
-
150
- ```js
151
- const request = {
152
- key: "example1.txt",
153
- content: Buffer.from("large data to upload")
154
- }
155
-
156
- const cost = await flatDirectory.estimateCost(request);
157
- console.log(`Gas Cost: ${cost.gasCost}, Storage Cost: ${cost.storageCost}`);
158
- ```
159
-
160
- Use `file`.
161
-
162
- Browser
163
- ```javascript
164
- // <input id='fileToUpload' />
165
- const file = document.getElementById('fileToUpload').files[0];
166
-
167
- const request = {
168
- key: "example1.txt",
169
- content: file
170
- }
171
- const cost = await flatDirectory.estimateCost(request);
172
- console.log(`Gas Cost: ${cost.gasCost}, Storage Cost: ${cost.storageCost}`);
173
- ```
174
-
175
- Node.js
176
- ```javascript
177
- const {NodeFile} = require("gethstorage-sdk/file");
178
- const file = new NodeFile("/usr/download/test.jpg");
179
-
180
- const request = {
181
- key: "example1.txt",
182
- content: file
183
- }
184
- const cost = await flatDirectory.estimateCost(request);
185
- ```
186
-
187
-
188
- ### Chunk options
189
-
190
- Uploads are sliced into calldata chunks. Both `estimateCost` and `upload`
191
- requests accept two optional fields:
192
-
193
- | Field | Type | Default | Description |
194
- |----------------|--------|---------|----------------------------------------------------------|
195
- | `maxChunkSize` | number | `64` | data size of each chunk, in KB |
196
- | `maxChunks` | number | `100` | maximum number of chunks requested in a single call |
197
-
198
- ```js
199
- const request = {
200
- key: "example1.txt",
201
- content: Buffer.from("large data to upload"),
202
- maxChunkSize: 32, // 32 KB per chunk
203
- maxChunks: 120 // at most 120 chunks per request
204
- }
205
- await flatDirectory.upload({ ...request, callback });
206
- ```
207
-
208
- `fetchHashes(keys, maxChunks)` accepts the same chunk limit as its second argument.
1
+ # gethstorage-sdk
2
+
3
+ This SDK aims to standardize the interaction between applications and the EthStorage network to achieve reliable and
4
+ efficient data management functionality.
5
+
6
+ This SDK provides the `FlatDirectory` class, a higher-level data management tool that provides methods for uploading
7
+ and downloading data of arbitrary size. Uploads are performed by sending the data through calldata.
8
+
9
+ Click here to view [spec](./spec.md).
10
+
11
+ # Installation
12
+
13
+ Install the SDK using [npm](https://www.npmjs.com/package/gethstorage-sdk):
14
+
15
+ ```bash
16
+ $ npm install gethstorage-sdk
17
+ ```
18
+
19
+ # Example Usage
20
+
21
+ ## FlatDirectory
22
+
23
+ ### create
24
+
25
+ Create a `FlatDirectory` instance.
26
+
27
+ ```js
28
+ const { FlatDirectory } = require("gethstorage-sdk");
29
+
30
+ const rpc = "https://rpc.beta.testnet.l2.quarkchain.io:8545";
31
+ const ethStorageRpc = "https://rpc.beta.testnet.l2.ethstorage.io:9596";
32
+ const privateKey = "0xabcd...";
33
+
34
+ const flatDirectory = await FlatDirectory.create({
35
+ rpc: rpc,
36
+ ethStorageRpc: ethStorageRpc,
37
+ privateKey: privateKey,
38
+ });
39
+ ```
40
+
41
+ If `FlatDirectory` has been deployed, it can be set through the `address` field.
42
+
43
+ ```js
44
+ const address = "0x987..."; // FlatDirectory address
45
+ const flatDirectory = await FlatDirectory.create({
46
+ rpc: rpc,
47
+ ethStorageRpc: ethStorageRpc,
48
+ privateKey: privateKey,
49
+ address: address,
50
+ });
51
+ ```
52
+
53
+ If you only need the `download` function, you can create a `FlatDirectory` using the following pattern.
54
+
55
+ ```js
56
+ const address = "0x987..."; // FlatDirectory address
57
+ const flatDirectory = await FlatDirectory.create({
58
+ ethStorageRpc: 'https://rpc.beta.testnet.l2.ethstorage.io:9596',
59
+ address: address
60
+ });
61
+ ```
62
+
63
+ ### deploy
64
+
65
+ Deploy the implementation
66
+ contract [FlatDirectory](https://github.com/ethstorage/evm-large-storage)
67
+ for [EIP-5018](https://eips.ethereum.org/EIPS/eip-5018) standard.
68
+
69
+ ```js
70
+ const contractAddress = await flatDirectory.deploy();
71
+ console.log(`FlatDirectory address is ${contractAddress}.`);
72
+ ```
73
+
74
+ ### upload
75
+
76
+ Upload `buffer | file` to the FlatDirectory. The content is sliced into chunks and each chunk is sent through calldata.
77
+
78
+ ```js
79
+ const callback = {
80
+ onProgress: function (progress, count, isChange) {
81
+ console.log(`Uploaded ${progress} of ${count} chunks`);
82
+ },
83
+ onFail: function (err) {
84
+ console.log(err);
85
+ },
86
+ onFinish: function (totalUploadChunks, totalUploadSize, totalCost) {
87
+ console.log(`Total upload chunk count is ${totalUploadChunks}, size is ${totalUploadSize}, total cost is ${totalCost}`);
88
+ }
89
+ };
90
+
91
+ const request = {
92
+ key: "test.txt",
93
+ content: Buffer.from("big data"),
94
+ callback: callback
95
+ }
96
+ await flatDirectory.upload(request);
97
+ ```
98
+
99
+ If you want to use `file`, it can be divided into browser and Node.js.
100
+
101
+ Browser
102
+ ```javascript
103
+ // <input id='fileToUpload' />
104
+ const file = document.getElementById('fileToUpload').files[0];
105
+
106
+ const request = {
107
+ key: "test.txt",
108
+ content: file,
109
+ callback: callback
110
+ }
111
+ await flatDirectory.upload(request);
112
+ ```
113
+
114
+ Node.js
115
+ ```javascript
116
+ const {NodeFile} = require("gethstorage-sdk/file");
117
+ const file = new NodeFile("/usr/download/test.jpg");
118
+
119
+ const request = {
120
+ key: "test.txt",
121
+ content: file,
122
+ callback: callback
123
+ }
124
+ await flatDirectory.upload(request);
125
+ ```
126
+
127
+ ### download
128
+
129
+ Monitor the download progress by passing in a callback function.
130
+
131
+ ```js
132
+ const key = "test.txt";
133
+ await flatDirectory.download(key, {
134
+ onProgress: function (progress, count, chunk) {
135
+ console.log(`Download ${progress} of ${count} chunks, this chunk is ${Buffer.from(chunk).toString()}`);
136
+ },
137
+ onFail: function (error) {
138
+ console.error("Error download data:", error);
139
+ },
140
+ onFinish: function () {
141
+ console.log("Download success.");
142
+ }
143
+ });
144
+ ```
145
+
146
+ ### estimateCost
147
+
148
+ Estimate gas costs before uploading.
149
+
150
+ ```js
151
+ const request = {
152
+ key: "example1.txt",
153
+ content: Buffer.from("large data to upload")
154
+ }
155
+
156
+ const cost = await flatDirectory.estimateCost(request);
157
+ console.log(`Gas Cost: ${cost.gasCost}, Storage Cost: ${cost.storageCost}`);
158
+ ```
159
+
160
+ Use `file`.
161
+
162
+ Browser
163
+ ```javascript
164
+ // <input id='fileToUpload' />
165
+ const file = document.getElementById('fileToUpload').files[0];
166
+
167
+ const request = {
168
+ key: "example1.txt",
169
+ content: file
170
+ }
171
+ const cost = await flatDirectory.estimateCost(request);
172
+ console.log(`Gas Cost: ${cost.gasCost}, Storage Cost: ${cost.storageCost}`);
173
+ ```
174
+
175
+ Node.js
176
+ ```javascript
177
+ const {NodeFile} = require("gethstorage-sdk/file");
178
+ const file = new NodeFile("/usr/download/test.jpg");
179
+
180
+ const request = {
181
+ key: "example1.txt",
182
+ content: file
183
+ }
184
+ const cost = await flatDirectory.estimateCost(request);
185
+ ```
186
+
187
+
188
+ ### Chunk options
189
+
190
+ Uploads are sliced into calldata chunks. Both `estimateCost` and `upload`
191
+ requests accept two optional fields:
192
+
193
+ | Field | Type | Default | Description |
194
+ |----------------|--------|---------|----------------------------------------------------------|
195
+ | `maxChunkSize` | number | `64` | data size of each chunk, in KB |
196
+ | `maxChunks` | number | `100` | maximum number of chunks requested in a single call |
197
+
198
+ ```js
199
+ const request = {
200
+ key: "example1.txt",
201
+ content: Buffer.from("large data to upload"),
202
+ maxChunkSize: 32, // 32 KB per chunk
203
+ maxChunks: 120 // at most 120 chunks per request
204
+ }
205
+ await flatDirectory.upload({ ...request, callback });
206
+ ```
207
+
208
+ `fetchHashes(keys, maxChunks)` accepts the same chunk limit as its second argument.
@@ -162,8 +162,44 @@ interface ChunkHashResult {
162
162
  }
163
163
 
164
164
  declare const stringToHex: (s: string) => string;
165
+ /**
166
+ * EIP-3541 forbids deploying new contract code whose first byte is 0xEF.
167
+ * EthStorage stores each chunk as a contract via CREATE2, so a chunk whose
168
+ * first byte is 0xEF will revert. We avoid this by shifting chunk boundaries.
169
+ */
170
+ declare const EF_MAGIC: number;
171
+ /**
172
+ * Number of bytes reserved at the end of each chunk as extension headroom.
173
+ *
174
+ * The base chunk size is reduced by this many bytes; when a boundary would
175
+ * leave the next chunk starting with 0xEF, the current chunk is extended by
176
+ * up to this many bytes to absorb the run. The extended size never exceeds
177
+ * the original (unreserved) chunk size, so the contract's maxChunkSize limit
178
+ * is never violated.
179
+ *
180
+ * Probability justification (uniform random bytes):
181
+ * P(16 consecutive 0xEF) = (1/256)^16 ≈ 1e-38, effectively zero.
182
+ * Even across billions of boundaries the probability remains negligible.
183
+ * If a longer run ever occurs, the shrink fallback handles it.
184
+ */
185
+ declare const EF_RESERVED_BYTES: number;
186
+ interface ChunkBoundary {
187
+ start: number;
188
+ end: number;
189
+ }
165
190
  declare function getChainId(rpc: string): Promise<number>;
166
191
  declare function getContentChunk(content: ContentLike, start: number, end: number): Promise<Uint8Array<ArrayBuffer>>;
192
+ /**
193
+ * Compute chunk boundaries for a calldata upload, shifting any boundary that
194
+ * would leave the next chunk starting with the 0xEF magic byte (EIP-3541).
195
+ *
196
+ * Strategy: for each boundary, if the byte at the boundary (first byte of the
197
+ * next chunk) is 0xEF, extend the current chunk to absorb it. If extension
198
+ * would exceed maxChunkSize, shrink the current chunk instead. In the extremely
199
+ * unlikely case that every byte in the adjustment window is 0xEF, the original
200
+ * fixed boundary is kept.
201
+ */
202
+ declare function computeCalldataChunkBoundaries(content: ContentLike, baseChunkSize: number, maxChunkSize: number): Promise<ChunkBoundary[]>;
167
203
  declare function isBuffer(content: ContentLike): content is BufferLike;
168
204
  declare function isFile(content: ContentLike): content is FileLike;
169
205
  declare function isNodejs(): boolean;
@@ -212,9 +248,13 @@ declare function getChunkHashes(contract: Contract, batch: FileBatch[]): Promise
212
248
 
213
249
  declare function stableRetry<T>(fn: (...args: unknown[]) => Promise<T>, ...args: unknown[]): Promise<T>;
214
250
 
251
+ type index_ChunkBoundary = ChunkBoundary;
252
+ declare const index_EF_MAGIC: typeof EF_MAGIC;
253
+ declare const index_EF_RESERVED_BYTES: typeof EF_RESERVED_BYTES;
215
254
  type index_TxSender = TxSender;
216
255
  declare const index_TxSender: typeof TxSender;
217
256
  declare const index_calcTxCost: typeof calcTxCost;
257
+ declare const index_computeCalldataChunkBoundaries: typeof computeCalldataChunkBoundaries;
218
258
  declare const index_getChainId: typeof getChainId;
219
259
  declare const index_getChunkCounts: typeof getChunkCounts;
220
260
  declare const index_getChunkHashes: typeof getChunkHashes;
@@ -226,7 +266,7 @@ declare const index_isNodejs: typeof isNodejs;
226
266
  declare const index_stableRetry: typeof stableRetry;
227
267
  declare const index_stringToHex: typeof stringToHex;
228
268
  declare namespace index {
229
- export { index_TxSender as TxSender, index_calcTxCost as calcTxCost, index_getChainId as getChainId, index_getChunkCounts as getChunkCounts, index_getChunkHashes as getChunkHashes, index_getContentChunk as getContentChunk, index_getUploadInfo as getUploadInfo, index_isBuffer as isBuffer, index_isFile as isFile, index_isNodejs as isNodejs, index_stableRetry as stableRetry, index_stringToHex as stringToHex };
269
+ export { type index_ChunkBoundary as ChunkBoundary, index_EF_MAGIC as EF_MAGIC, index_EF_RESERVED_BYTES as EF_RESERVED_BYTES, index_TxSender as TxSender, index_calcTxCost as calcTxCost, index_computeCalldataChunkBoundaries as computeCalldataChunkBoundaries, index_getChainId as getChainId, index_getChunkCounts as getChunkCounts, index_getChunkHashes as getChunkHashes, index_getContentChunk as getContentChunk, index_getUploadInfo as getUploadInfo, index_isBuffer as isBuffer, index_isFile as isFile, index_isNodejs as isNodejs, index_stableRetry as stableRetry, index_stringToHex as stringToHex };
230
270
  }
231
271
 
232
272
  declare class FlatDirectory {
@@ -7,8 +7,11 @@ var __export = (target, all) => {
7
7
  // src/utils/index.ts
8
8
  var utils_exports = {};
9
9
  __export(utils_exports, {
10
+ EF_MAGIC: () => EF_MAGIC,
11
+ EF_RESERVED_BYTES: () => EF_RESERVED_BYTES,
10
12
  TxSender: () => TxSender,
11
13
  calcTxCost: () => calcTxCost,
14
+ computeCalldataChunkBoundaries: () => computeCalldataChunkBoundaries,
12
15
  getChainId: () => getChainId,
13
16
  getChunkCounts: () => getChunkCounts,
14
17
  getChunkHashes: () => getChunkHashes,
@@ -24,6 +27,8 @@ __export(utils_exports, {
24
27
  // src/utils/util.ts
25
28
  import { ethers } from "ethers";
26
29
  var stringToHex = (s) => ethers.hexlify(ethers.toUtf8Bytes(s));
30
+ var EF_MAGIC = 239;
31
+ var EF_RESERVED_BYTES = 16;
27
32
  async function getChainId(rpc) {
28
33
  const provider = new ethers.JsonRpcProvider(rpc);
29
34
  const network = await provider.getNetwork();
@@ -38,6 +43,50 @@ async function getContentChunk(content, start, end) {
38
43
  return new Uint8Array(data);
39
44
  }
40
45
  }
46
+ async function computeCalldataChunkBoundaries(content, baseChunkSize, maxChunkSize) {
47
+ const size = isFile(content) ? content.size : content.length;
48
+ const boundaries = [];
49
+ let pos = 0;
50
+ while (pos < size) {
51
+ let end = Math.min(pos + baseChunkSize, size);
52
+ if (end < size) {
53
+ const maxExtendEnd = Math.min(pos + maxChunkSize, size);
54
+ if (maxExtendEnd > end) {
55
+ const extWindow = await getContentChunk(content, end, maxExtendEnd);
56
+ let safeOffset = -1;
57
+ for (let i = 0; i < extWindow.length; i++) {
58
+ if (extWindow[i] !== EF_MAGIC) {
59
+ safeOffset = i;
60
+ break;
61
+ }
62
+ }
63
+ if (safeOffset >= 0) {
64
+ end = end + safeOffset;
65
+ }
66
+ }
67
+ if (end < size) {
68
+ const firstByte = await getContentChunk(content, end, end + 1);
69
+ if (firstByte.length > 0 && firstByte[0] === EF_MAGIC) {
70
+ const shrinkStart = Math.max(pos, end - 256);
71
+ const shrinkWindow = await getContentChunk(content, shrinkStart, end);
72
+ let lastSafe = -1;
73
+ for (let i = shrinkWindow.length - 1; i >= 0; i--) {
74
+ if (shrinkWindow[i] !== EF_MAGIC) {
75
+ lastSafe = shrinkStart + i;
76
+ break;
77
+ }
78
+ }
79
+ if (lastSafe > pos) {
80
+ end = lastSafe;
81
+ }
82
+ }
83
+ }
84
+ }
85
+ boundaries.push({ start: pos, end });
86
+ pos = end;
87
+ }
88
+ return boundaries;
89
+ }
41
90
  function isBuffer(content) {
42
91
  return content instanceof Uint8Array;
43
92
  }
@@ -762,10 +811,13 @@ var FlatDirectory = class _FlatDirectory {
762
811
  if (toChunkSizeBytes(maxChunkSize) > CONTRACT_MAX_CHUNK_SIZE) {
763
812
  throw new Error(`FlatDirectory: Chunk size ${maxChunkSize} KB exceeds the on-chain max payload of ${CONTRACT_MAX_CHUNK_SIZE} bytes.`);
764
813
  }
765
- const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
814
+ const { chunkDataSize } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
766
815
  if (chunkDataSize === -1) {
767
816
  throw new Error(`FlatDirectory: Invalid upload content!`);
768
817
  }
818
+ const baseChunkSize = Math.max(1, chunkDataSize - EF_RESERVED_BYTES);
819
+ const boundaries = await computeCalldataChunkBoundaries(content, baseChunkSize, chunkDataSize);
820
+ const calldataChunkCount = boundaries.length;
769
821
  const hexName = stringToHex(key);
770
822
  const fileContract = new ethers3.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
771
823
  const [{ oldChunkCount, fileMode }, resolvedFees] = await Promise.all([
@@ -791,7 +843,8 @@ var FlatDirectory = class _FlatDirectory {
791
843
  () => fileContract["writeChunkByCalldata"].estimateGas(hexName, chunkId, hexData)
792
844
  );
793
845
  for (let i = 0; i < calldataChunkCount; i++) {
794
- const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
846
+ const { start, end } = boundaries[i];
847
+ const chunk = await getContentChunk(content, start, end);
795
848
  if (i < chunkHashes.length && ethers3.keccak256(chunk) === chunkHashes[i]) {
796
849
  continue;
797
850
  }
@@ -830,12 +883,15 @@ var FlatDirectory = class _FlatDirectory {
830
883
  callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
831
884
  return;
832
885
  }
833
- const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
886
+ const { chunkDataSize } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
834
887
  if (chunkDataSize === -1) {
835
888
  callback.onFail(new Error(`FlatDirectory: Invalid upload content!`));
836
889
  callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
837
890
  return;
838
891
  }
892
+ const baseChunkSize = Math.max(1, chunkDataSize - EF_RESERVED_BYTES);
893
+ const boundaries = await computeCalldataChunkBoundaries(content, baseChunkSize, chunkDataSize);
894
+ const calldataChunkCount = boundaries.length;
839
895
  const hexName = stringToHex(key);
840
896
  const fileContract = new ethers3.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
841
897
  const { oldChunkCount, fileMode } = await getUploadInfo(fileContract, hexName);
@@ -876,7 +932,8 @@ var FlatDirectory = class _FlatDirectory {
876
932
  }
877
933
  }
878
934
  for (let i = 0; i < calldataChunkCount; i++) {
879
- const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
935
+ const { start, end } = boundaries[i];
936
+ const chunk = await getContentChunk(content, start, end);
880
937
  if (i < chunkHashes.length && ethers3.keccak256(chunk) === chunkHashes[i]) {
881
938
  callback.onProgress(i, calldataChunkCount, false);
882
939
  continue;
@@ -52,8 +52,11 @@ module.exports = __toCommonJS(index_exports);
52
52
  // src/utils/index.ts
53
53
  var utils_exports = {};
54
54
  __export(utils_exports, {
55
+ EF_MAGIC: () => EF_MAGIC,
56
+ EF_RESERVED_BYTES: () => EF_RESERVED_BYTES,
55
57
  TxSender: () => TxSender,
56
58
  calcTxCost: () => calcTxCost,
59
+ computeCalldataChunkBoundaries: () => computeCalldataChunkBoundaries,
57
60
  getChainId: () => getChainId,
58
61
  getChunkCounts: () => getChunkCounts,
59
62
  getChunkHashes: () => getChunkHashes,
@@ -69,6 +72,8 @@ __export(utils_exports, {
69
72
  // src/utils/util.ts
70
73
  var import_ethers = require("ethers");
71
74
  var stringToHex = (s) => import_ethers.ethers.hexlify(import_ethers.ethers.toUtf8Bytes(s));
75
+ var EF_MAGIC = 239;
76
+ var EF_RESERVED_BYTES = 16;
72
77
  async function getChainId(rpc) {
73
78
  const provider = new import_ethers.ethers.JsonRpcProvider(rpc);
74
79
  const network = await provider.getNetwork();
@@ -83,6 +88,50 @@ async function getContentChunk(content, start, end) {
83
88
  return new Uint8Array(data);
84
89
  }
85
90
  }
91
+ async function computeCalldataChunkBoundaries(content, baseChunkSize, maxChunkSize) {
92
+ const size = isFile(content) ? content.size : content.length;
93
+ const boundaries = [];
94
+ let pos = 0;
95
+ while (pos < size) {
96
+ let end = Math.min(pos + baseChunkSize, size);
97
+ if (end < size) {
98
+ const maxExtendEnd = Math.min(pos + maxChunkSize, size);
99
+ if (maxExtendEnd > end) {
100
+ const extWindow = await getContentChunk(content, end, maxExtendEnd);
101
+ let safeOffset = -1;
102
+ for (let i = 0; i < extWindow.length; i++) {
103
+ if (extWindow[i] !== EF_MAGIC) {
104
+ safeOffset = i;
105
+ break;
106
+ }
107
+ }
108
+ if (safeOffset >= 0) {
109
+ end = end + safeOffset;
110
+ }
111
+ }
112
+ if (end < size) {
113
+ const firstByte = await getContentChunk(content, end, end + 1);
114
+ if (firstByte.length > 0 && firstByte[0] === EF_MAGIC) {
115
+ const shrinkStart = Math.max(pos, end - 256);
116
+ const shrinkWindow = await getContentChunk(content, shrinkStart, end);
117
+ let lastSafe = -1;
118
+ for (let i = shrinkWindow.length - 1; i >= 0; i--) {
119
+ if (shrinkWindow[i] !== EF_MAGIC) {
120
+ lastSafe = shrinkStart + i;
121
+ break;
122
+ }
123
+ }
124
+ if (lastSafe > pos) {
125
+ end = lastSafe;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ boundaries.push({ start: pos, end });
131
+ pos = end;
132
+ }
133
+ return boundaries;
134
+ }
86
135
  function isBuffer(content) {
87
136
  return content instanceof Uint8Array;
88
137
  }
@@ -807,10 +856,13 @@ var FlatDirectory = class _FlatDirectory {
807
856
  if (toChunkSizeBytes(maxChunkSize) > CONTRACT_MAX_CHUNK_SIZE) {
808
857
  throw new Error(`FlatDirectory: Chunk size ${maxChunkSize} KB exceeds the on-chain max payload of ${CONTRACT_MAX_CHUNK_SIZE} bytes.`);
809
858
  }
810
- const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
859
+ const { chunkDataSize } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
811
860
  if (chunkDataSize === -1) {
812
861
  throw new Error(`FlatDirectory: Invalid upload content!`);
813
862
  }
863
+ const baseChunkSize = Math.max(1, chunkDataSize - EF_RESERVED_BYTES);
864
+ const boundaries = await computeCalldataChunkBoundaries(content, baseChunkSize, chunkDataSize);
865
+ const calldataChunkCount = boundaries.length;
814
866
  const hexName = stringToHex(key);
815
867
  const fileContract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
816
868
  const [{ oldChunkCount, fileMode }, resolvedFees] = await Promise.all([
@@ -836,7 +888,8 @@ var FlatDirectory = class _FlatDirectory {
836
888
  () => fileContract["writeChunkByCalldata"].estimateGas(hexName, chunkId, hexData)
837
889
  );
838
890
  for (let i = 0; i < calldataChunkCount; i++) {
839
- const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
891
+ const { start, end } = boundaries[i];
892
+ const chunk = await getContentChunk(content, start, end);
840
893
  if (i < chunkHashes.length && import_ethers3.ethers.keccak256(chunk) === chunkHashes[i]) {
841
894
  continue;
842
895
  }
@@ -875,12 +928,15 @@ var FlatDirectory = class _FlatDirectory {
875
928
  callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
876
929
  return;
877
930
  }
878
- const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
931
+ const { chunkDataSize } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
879
932
  if (chunkDataSize === -1) {
880
933
  callback.onFail(new Error(`FlatDirectory: Invalid upload content!`));
881
934
  callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
882
935
  return;
883
936
  }
937
+ const baseChunkSize = Math.max(1, chunkDataSize - EF_RESERVED_BYTES);
938
+ const boundaries = await computeCalldataChunkBoundaries(content, baseChunkSize, chunkDataSize);
939
+ const calldataChunkCount = boundaries.length;
884
940
  const hexName = stringToHex(key);
885
941
  const fileContract = new import_ethers3.ethers.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
886
942
  const { oldChunkCount, fileMode } = await getUploadInfo(fileContract, hexName);
@@ -921,7 +977,8 @@ var FlatDirectory = class _FlatDirectory {
921
977
  }
922
978
  }
923
979
  for (let i = 0; i < calldataChunkCount; i++) {
924
- const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
980
+ const { start, end } = boundaries[i];
981
+ const chunk = await getContentChunk(content, start, end);
925
982
  if (i < chunkHashes.length && import_ethers3.ethers.keccak256(chunk) === chunkHashes[i]) {
926
983
  callback.onProgress(i, calldataChunkCount, false);
927
984
  continue;
@@ -162,8 +162,44 @@ interface ChunkHashResult {
162
162
  }
163
163
 
164
164
  declare const stringToHex: (s: string) => string;
165
+ /**
166
+ * EIP-3541 forbids deploying new contract code whose first byte is 0xEF.
167
+ * EthStorage stores each chunk as a contract via CREATE2, so a chunk whose
168
+ * first byte is 0xEF will revert. We avoid this by shifting chunk boundaries.
169
+ */
170
+ declare const EF_MAGIC: number;
171
+ /**
172
+ * Number of bytes reserved at the end of each chunk as extension headroom.
173
+ *
174
+ * The base chunk size is reduced by this many bytes; when a boundary would
175
+ * leave the next chunk starting with 0xEF, the current chunk is extended by
176
+ * up to this many bytes to absorb the run. The extended size never exceeds
177
+ * the original (unreserved) chunk size, so the contract's maxChunkSize limit
178
+ * is never violated.
179
+ *
180
+ * Probability justification (uniform random bytes):
181
+ * P(16 consecutive 0xEF) = (1/256)^16 ≈ 1e-38, effectively zero.
182
+ * Even across billions of boundaries the probability remains negligible.
183
+ * If a longer run ever occurs, the shrink fallback handles it.
184
+ */
185
+ declare const EF_RESERVED_BYTES: number;
186
+ interface ChunkBoundary {
187
+ start: number;
188
+ end: number;
189
+ }
165
190
  declare function getChainId(rpc: string): Promise<number>;
166
191
  declare function getContentChunk(content: ContentLike, start: number, end: number): Promise<Uint8Array<ArrayBuffer>>;
192
+ /**
193
+ * Compute chunk boundaries for a calldata upload, shifting any boundary that
194
+ * would leave the next chunk starting with the 0xEF magic byte (EIP-3541).
195
+ *
196
+ * Strategy: for each boundary, if the byte at the boundary (first byte of the
197
+ * next chunk) is 0xEF, extend the current chunk to absorb it. If extension
198
+ * would exceed maxChunkSize, shrink the current chunk instead. In the extremely
199
+ * unlikely case that every byte in the adjustment window is 0xEF, the original
200
+ * fixed boundary is kept.
201
+ */
202
+ declare function computeCalldataChunkBoundaries(content: ContentLike, baseChunkSize: number, maxChunkSize: number): Promise<ChunkBoundary[]>;
167
203
  declare function isBuffer(content: ContentLike): content is BufferLike;
168
204
  declare function isFile(content: ContentLike): content is FileLike;
169
205
  declare function isNodejs(): boolean;
@@ -212,9 +248,13 @@ declare function getChunkHashes(contract: Contract, batch: FileBatch[]): Promise
212
248
 
213
249
  declare function stableRetry<T>(fn: (...args: unknown[]) => Promise<T>, ...args: unknown[]): Promise<T>;
214
250
 
251
+ type index_ChunkBoundary = ChunkBoundary;
252
+ declare const index_EF_MAGIC: typeof EF_MAGIC;
253
+ declare const index_EF_RESERVED_BYTES: typeof EF_RESERVED_BYTES;
215
254
  type index_TxSender = TxSender;
216
255
  declare const index_TxSender: typeof TxSender;
217
256
  declare const index_calcTxCost: typeof calcTxCost;
257
+ declare const index_computeCalldataChunkBoundaries: typeof computeCalldataChunkBoundaries;
218
258
  declare const index_getChainId: typeof getChainId;
219
259
  declare const index_getChunkCounts: typeof getChunkCounts;
220
260
  declare const index_getChunkHashes: typeof getChunkHashes;
@@ -226,7 +266,7 @@ declare const index_isNodejs: typeof isNodejs;
226
266
  declare const index_stableRetry: typeof stableRetry;
227
267
  declare const index_stringToHex: typeof stringToHex;
228
268
  declare namespace index {
229
- export { index_TxSender as TxSender, index_calcTxCost as calcTxCost, index_getChainId as getChainId, index_getChunkCounts as getChunkCounts, index_getChunkHashes as getChunkHashes, index_getContentChunk as getContentChunk, index_getUploadInfo as getUploadInfo, index_isBuffer as isBuffer, index_isFile as isFile, index_isNodejs as isNodejs, index_stableRetry as stableRetry, index_stringToHex as stringToHex };
269
+ export { type index_ChunkBoundary as ChunkBoundary, index_EF_MAGIC as EF_MAGIC, index_EF_RESERVED_BYTES as EF_RESERVED_BYTES, index_TxSender as TxSender, index_calcTxCost as calcTxCost, index_computeCalldataChunkBoundaries as computeCalldataChunkBoundaries, index_getChainId as getChainId, index_getChunkCounts as getChunkCounts, index_getChunkHashes as getChunkHashes, index_getContentChunk as getContentChunk, index_getUploadInfo as getUploadInfo, index_isBuffer as isBuffer, index_isFile as isFile, index_isNodejs as isNodejs, index_stableRetry as stableRetry, index_stringToHex as stringToHex };
230
270
  }
231
271
 
232
272
  declare class FlatDirectory {
@@ -162,8 +162,44 @@ interface ChunkHashResult {
162
162
  }
163
163
 
164
164
  declare const stringToHex: (s: string) => string;
165
+ /**
166
+ * EIP-3541 forbids deploying new contract code whose first byte is 0xEF.
167
+ * EthStorage stores each chunk as a contract via CREATE2, so a chunk whose
168
+ * first byte is 0xEF will revert. We avoid this by shifting chunk boundaries.
169
+ */
170
+ declare const EF_MAGIC: number;
171
+ /**
172
+ * Number of bytes reserved at the end of each chunk as extension headroom.
173
+ *
174
+ * The base chunk size is reduced by this many bytes; when a boundary would
175
+ * leave the next chunk starting with 0xEF, the current chunk is extended by
176
+ * up to this many bytes to absorb the run. The extended size never exceeds
177
+ * the original (unreserved) chunk size, so the contract's maxChunkSize limit
178
+ * is never violated.
179
+ *
180
+ * Probability justification (uniform random bytes):
181
+ * P(16 consecutive 0xEF) = (1/256)^16 ≈ 1e-38, effectively zero.
182
+ * Even across billions of boundaries the probability remains negligible.
183
+ * If a longer run ever occurs, the shrink fallback handles it.
184
+ */
185
+ declare const EF_RESERVED_BYTES: number;
186
+ interface ChunkBoundary {
187
+ start: number;
188
+ end: number;
189
+ }
165
190
  declare function getChainId(rpc: string): Promise<number>;
166
191
  declare function getContentChunk(content: ContentLike, start: number, end: number): Promise<Uint8Array<ArrayBuffer>>;
192
+ /**
193
+ * Compute chunk boundaries for a calldata upload, shifting any boundary that
194
+ * would leave the next chunk starting with the 0xEF magic byte (EIP-3541).
195
+ *
196
+ * Strategy: for each boundary, if the byte at the boundary (first byte of the
197
+ * next chunk) is 0xEF, extend the current chunk to absorb it. If extension
198
+ * would exceed maxChunkSize, shrink the current chunk instead. In the extremely
199
+ * unlikely case that every byte in the adjustment window is 0xEF, the original
200
+ * fixed boundary is kept.
201
+ */
202
+ declare function computeCalldataChunkBoundaries(content: ContentLike, baseChunkSize: number, maxChunkSize: number): Promise<ChunkBoundary[]>;
167
203
  declare function isBuffer(content: ContentLike): content is BufferLike;
168
204
  declare function isFile(content: ContentLike): content is FileLike;
169
205
  declare function isNodejs(): boolean;
@@ -212,9 +248,13 @@ declare function getChunkHashes(contract: Contract, batch: FileBatch[]): Promise
212
248
 
213
249
  declare function stableRetry<T>(fn: (...args: unknown[]) => Promise<T>, ...args: unknown[]): Promise<T>;
214
250
 
251
+ type index_ChunkBoundary = ChunkBoundary;
252
+ declare const index_EF_MAGIC: typeof EF_MAGIC;
253
+ declare const index_EF_RESERVED_BYTES: typeof EF_RESERVED_BYTES;
215
254
  type index_TxSender = TxSender;
216
255
  declare const index_TxSender: typeof TxSender;
217
256
  declare const index_calcTxCost: typeof calcTxCost;
257
+ declare const index_computeCalldataChunkBoundaries: typeof computeCalldataChunkBoundaries;
218
258
  declare const index_getChainId: typeof getChainId;
219
259
  declare const index_getChunkCounts: typeof getChunkCounts;
220
260
  declare const index_getChunkHashes: typeof getChunkHashes;
@@ -226,7 +266,7 @@ declare const index_isNodejs: typeof isNodejs;
226
266
  declare const index_stableRetry: typeof stableRetry;
227
267
  declare const index_stringToHex: typeof stringToHex;
228
268
  declare namespace index {
229
- export { index_TxSender as TxSender, index_calcTxCost as calcTxCost, index_getChainId as getChainId, index_getChunkCounts as getChunkCounts, index_getChunkHashes as getChunkHashes, index_getContentChunk as getContentChunk, index_getUploadInfo as getUploadInfo, index_isBuffer as isBuffer, index_isFile as isFile, index_isNodejs as isNodejs, index_stableRetry as stableRetry, index_stringToHex as stringToHex };
269
+ export { type index_ChunkBoundary as ChunkBoundary, index_EF_MAGIC as EF_MAGIC, index_EF_RESERVED_BYTES as EF_RESERVED_BYTES, index_TxSender as TxSender, index_calcTxCost as calcTxCost, index_computeCalldataChunkBoundaries as computeCalldataChunkBoundaries, index_getChainId as getChainId, index_getChunkCounts as getChunkCounts, index_getChunkHashes as getChunkHashes, index_getContentChunk as getContentChunk, index_getUploadInfo as getUploadInfo, index_isBuffer as isBuffer, index_isFile as isFile, index_isNodejs as isNodejs, index_stableRetry as stableRetry, index_stringToHex as stringToHex };
230
270
  }
231
271
 
232
272
  declare class FlatDirectory {
@@ -7,8 +7,11 @@ var __export = (target, all) => {
7
7
  // src/utils/index.ts
8
8
  var utils_exports = {};
9
9
  __export(utils_exports, {
10
+ EF_MAGIC: () => EF_MAGIC,
11
+ EF_RESERVED_BYTES: () => EF_RESERVED_BYTES,
10
12
  TxSender: () => TxSender,
11
13
  calcTxCost: () => calcTxCost,
14
+ computeCalldataChunkBoundaries: () => computeCalldataChunkBoundaries,
12
15
  getChainId: () => getChainId,
13
16
  getChunkCounts: () => getChunkCounts,
14
17
  getChunkHashes: () => getChunkHashes,
@@ -24,6 +27,8 @@ __export(utils_exports, {
24
27
  // src/utils/util.ts
25
28
  import { ethers } from "ethers";
26
29
  var stringToHex = (s) => ethers.hexlify(ethers.toUtf8Bytes(s));
30
+ var EF_MAGIC = 239;
31
+ var EF_RESERVED_BYTES = 16;
27
32
  async function getChainId(rpc) {
28
33
  const provider = new ethers.JsonRpcProvider(rpc);
29
34
  const network = await provider.getNetwork();
@@ -38,6 +43,50 @@ async function getContentChunk(content, start, end) {
38
43
  return new Uint8Array(data);
39
44
  }
40
45
  }
46
+ async function computeCalldataChunkBoundaries(content, baseChunkSize, maxChunkSize) {
47
+ const size = isFile(content) ? content.size : content.length;
48
+ const boundaries = [];
49
+ let pos = 0;
50
+ while (pos < size) {
51
+ let end = Math.min(pos + baseChunkSize, size);
52
+ if (end < size) {
53
+ const maxExtendEnd = Math.min(pos + maxChunkSize, size);
54
+ if (maxExtendEnd > end) {
55
+ const extWindow = await getContentChunk(content, end, maxExtendEnd);
56
+ let safeOffset = -1;
57
+ for (let i = 0; i < extWindow.length; i++) {
58
+ if (extWindow[i] !== EF_MAGIC) {
59
+ safeOffset = i;
60
+ break;
61
+ }
62
+ }
63
+ if (safeOffset >= 0) {
64
+ end = end + safeOffset;
65
+ }
66
+ }
67
+ if (end < size) {
68
+ const firstByte = await getContentChunk(content, end, end + 1);
69
+ if (firstByte.length > 0 && firstByte[0] === EF_MAGIC) {
70
+ const shrinkStart = Math.max(pos, end - 256);
71
+ const shrinkWindow = await getContentChunk(content, shrinkStart, end);
72
+ let lastSafe = -1;
73
+ for (let i = shrinkWindow.length - 1; i >= 0; i--) {
74
+ if (shrinkWindow[i] !== EF_MAGIC) {
75
+ lastSafe = shrinkStart + i;
76
+ break;
77
+ }
78
+ }
79
+ if (lastSafe > pos) {
80
+ end = lastSafe;
81
+ }
82
+ }
83
+ }
84
+ }
85
+ boundaries.push({ start: pos, end });
86
+ pos = end;
87
+ }
88
+ return boundaries;
89
+ }
41
90
  function isBuffer(content) {
42
91
  return content instanceof Uint8Array;
43
92
  }
@@ -762,10 +811,13 @@ var FlatDirectory = class _FlatDirectory {
762
811
  if (toChunkSizeBytes(maxChunkSize) > CONTRACT_MAX_CHUNK_SIZE) {
763
812
  throw new Error(`FlatDirectory: Chunk size ${maxChunkSize} KB exceeds the on-chain max payload of ${CONTRACT_MAX_CHUNK_SIZE} bytes.`);
764
813
  }
765
- const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
814
+ const { chunkDataSize } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
766
815
  if (chunkDataSize === -1) {
767
816
  throw new Error(`FlatDirectory: Invalid upload content!`);
768
817
  }
818
+ const baseChunkSize = Math.max(1, chunkDataSize - EF_RESERVED_BYTES);
819
+ const boundaries = await computeCalldataChunkBoundaries(content, baseChunkSize, chunkDataSize);
820
+ const calldataChunkCount = boundaries.length;
769
821
  const hexName = stringToHex(key);
770
822
  const fileContract = new ethers3.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
771
823
  const [{ oldChunkCount, fileMode }, resolvedFees] = await Promise.all([
@@ -791,7 +843,8 @@ var FlatDirectory = class _FlatDirectory {
791
843
  () => fileContract["writeChunkByCalldata"].estimateGas(hexName, chunkId, hexData)
792
844
  );
793
845
  for (let i = 0; i < calldataChunkCount; i++) {
794
- const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
846
+ const { start, end } = boundaries[i];
847
+ const chunk = await getContentChunk(content, start, end);
795
848
  if (i < chunkHashes.length && ethers3.keccak256(chunk) === chunkHashes[i]) {
796
849
  continue;
797
850
  }
@@ -830,12 +883,15 @@ var FlatDirectory = class _FlatDirectory {
830
883
  callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
831
884
  return;
832
885
  }
833
- const { chunkDataSize, calldataChunkCount } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
886
+ const { chunkDataSize } = this.#calculateCalldataChunkDetails(content, maxChunkSize);
834
887
  if (chunkDataSize === -1) {
835
888
  callback.onFail(new Error(`FlatDirectory: Invalid upload content!`));
836
889
  callback.onFinish(totalUploadChunks, totalUploadSize, totalCost);
837
890
  return;
838
891
  }
892
+ const baseChunkSize = Math.max(1, chunkDataSize - EF_RESERVED_BYTES);
893
+ const boundaries = await computeCalldataChunkBoundaries(content, baseChunkSize, chunkDataSize);
894
+ const calldataChunkCount = boundaries.length;
839
895
  const hexName = stringToHex(key);
840
896
  const fileContract = new ethers3.Contract(this.#contractAddrChecked, FlatDirectoryAbi, this.#walletChecked);
841
897
  const { oldChunkCount, fileMode } = await getUploadInfo(fileContract, hexName);
@@ -876,7 +932,8 @@ var FlatDirectory = class _FlatDirectory {
876
932
  }
877
933
  }
878
934
  for (let i = 0; i < calldataChunkCount; i++) {
879
- const chunk = await getContentChunk(content, i * chunkDataSize, (i + 1) * chunkDataSize);
935
+ const { start, end } = boundaries[i];
936
+ const chunk = await getContentChunk(content, start, end);
880
937
  if (i < chunkHashes.length && ethers3.keccak256(chunk) === chunkHashes[i]) {
881
938
  callback.onProgress(i, calldataChunkCount, false);
882
939
  continue;
package/package.json CHANGED
@@ -1,65 +1,65 @@
1
- {
2
- "name": "gethstorage-sdk",
3
- "version": "5.0.2",
4
- "description": "EthStorage flat directory upload sdk (calldata only)",
5
- "files": [
6
- "dist"
7
- ],
8
- "main": "./dist/node/index.cjs",
9
- "types": "./dist/node/index.d.ts",
10
- "exports": {
11
- ".": {
12
- "browser": "./dist/browser/index.mjs",
13
- "node": {
14
- "import": "./dist/node/index.mjs",
15
- "require": "./dist/node/index.cjs"
16
- },
17
- "types": "./dist/node/index.d.ts",
18
- "default": "./dist/node/index.mjs"
19
- },
20
- "./file": {
21
- "node": {
22
- "import": "./dist/node/file.mjs",
23
- "require": "./dist/node/file.cjs"
24
- },
25
- "types": "./dist/node/file.d.ts"
26
- }
27
- },
28
- "scripts": {
29
- "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
30
- "dev": "tsup --watch --env.NODE_ENV development",
31
- "prepublishOnly": "npm run build"
32
- },
33
- "dependencies": {
34
- "dotenv": "^16.4.5",
35
- "ethers": "^6.17.0",
36
- "p-limit": "^7.2.0"
37
- },
38
- "repository": {
39
- "type": "git",
40
- "url": "git+https://github.com/0xCampanula/gethstorage-sdk.git"
41
- },
42
- "bugs": {
43
- "url": "https://github.com/0xCampanula/gethstorage-sdk/issues"
44
- },
45
- "homepage": "https://github.com/0xCampanula/gethstorage-sdk/main/",
46
- "keywords": [
47
- "File",
48
- "Upload"
49
- ],
50
- "author": "ethsorage",
51
- "license": "ISC",
52
- "devDependencies": {
53
- "tsup": "^8.5.1",
54
- "typescript": "^5.9.3"
55
- },
56
- "resolutions": {
57
- "strip-ansi": "7.1.0",
58
- "color-convert": "2.0.1",
59
- "color-name": "1.1.4"
60
- },
61
- "overrides": {
62
- "rollup": ">=4.59.0",
63
- "picomatch": ">=4.0.4"
64
- }
65
- }
1
+ {
2
+ "name": "gethstorage-sdk",
3
+ "version": "5.0.3",
4
+ "description": "EthStorage flat directory upload sdk (calldata only)",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "main": "./dist/node/index.cjs",
9
+ "types": "./dist/node/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "browser": "./dist/browser/index.mjs",
13
+ "node": {
14
+ "import": "./dist/node/index.mjs",
15
+ "require": "./dist/node/index.cjs"
16
+ },
17
+ "types": "./dist/node/index.d.ts",
18
+ "default": "./dist/node/index.mjs"
19
+ },
20
+ "./file": {
21
+ "node": {
22
+ "import": "./dist/node/file.mjs",
23
+ "require": "./dist/node/file.cjs"
24
+ },
25
+ "types": "./dist/node/file.d.ts"
26
+ }
27
+ },
28
+ "scripts": {
29
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
30
+ "dev": "tsup --watch --env.NODE_ENV development",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "dependencies": {
34
+ "dotenv": "^16.4.5",
35
+ "ethers": "^6.17.0",
36
+ "p-limit": "^7.2.0"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/0xCampanula/gethstorage-sdk.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/0xCampanula/gethstorage-sdk/issues"
44
+ },
45
+ "homepage": "https://github.com/0xCampanula/gethstorage-sdk/main/",
46
+ "keywords": [
47
+ "File",
48
+ "Upload"
49
+ ],
50
+ "author": "ethsorage",
51
+ "license": "ISC",
52
+ "devDependencies": {
53
+ "tsup": "^8.5.1",
54
+ "typescript": "^5.9.3"
55
+ },
56
+ "resolutions": {
57
+ "strip-ansi": "7.1.0",
58
+ "color-convert": "2.0.1",
59
+ "color-name": "1.1.4"
60
+ },
61
+ "overrides": {
62
+ "rollup": ">=4.59.0",
63
+ "picomatch": ">=4.0.4"
64
+ }
65
+ }