@shelby-protocol/sdk 0.3.0 → 0.4.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 (126) hide show
  1. package/dist/browser/index.d.ts +19 -10
  2. package/dist/browser/index.mjs +4200 -146
  3. package/dist/core/aptos-explorer.mjs +46 -5
  4. package/dist/core/blobs.mjs +12 -4
  5. package/dist/core/chunk.mjs +58 -9
  6. package/dist/core/clients/ShelbyBlobClient.d.ts +155 -83
  7. package/dist/core/clients/ShelbyBlobClient.mjs +1286 -17
  8. package/dist/core/clients/ShelbyClient.d.ts +39 -6
  9. package/dist/core/clients/ShelbyClient.mjs +3233 -23
  10. package/dist/core/clients/ShelbyClientConfig.d.ts +9 -1
  11. package/dist/core/clients/ShelbyClientConfig.mjs +0 -1
  12. package/dist/core/clients/ShelbyMetadataClient.d.ts +20 -6
  13. package/dist/core/clients/ShelbyMetadataClient.mjs +388 -6
  14. package/dist/core/clients/ShelbyMicropaymentChannelClient.d.ts +7 -7
  15. package/dist/core/clients/ShelbyMicropaymentChannelClient.mjs +894 -13
  16. package/dist/core/clients/ShelbyPlacementGroupClient.mjs +310 -8
  17. package/dist/core/clients/ShelbyRPCClient.d.ts +117 -25
  18. package/dist/core/clients/ShelbyRPCClient.mjs +1245 -12
  19. package/dist/core/clients/index.d.ts +4 -2
  20. package/dist/core/clients/index.mjs +3802 -39
  21. package/dist/core/clients/utils.mjs +65 -6
  22. package/dist/core/commitments.d.ts +12 -1
  23. package/dist/core/commitments.mjs +277 -11
  24. package/dist/core/constants.d.ts +3 -3
  25. package/dist/core/constants.mjs +39 -16
  26. package/dist/core/erasure/clay-codes.mjs +129 -4
  27. package/dist/core/erasure/constants.d.ts +16 -1
  28. package/dist/core/erasure/constants.mjs +48 -11
  29. package/dist/core/erasure/default.mjs +191 -9
  30. package/dist/core/erasure/index.d.ts +1 -1
  31. package/dist/core/erasure/index.mjs +281 -24
  32. package/dist/core/erasure/reed-solomon.mjs +68 -4
  33. package/dist/core/erasure/utils.mjs +0 -2
  34. package/dist/core/errors.d.ts +20 -1
  35. package/dist/core/errors.mjs +58 -8
  36. package/dist/core/index.d.ts +9 -9
  37. package/dist/core/index.mjs +4194 -146
  38. package/dist/core/layout.mjs +37 -5
  39. package/dist/core/networks.mjs +10 -5
  40. package/dist/core/operations/generated/sdk.d.ts +807 -42
  41. package/dist/core/operations/generated/sdk.mjs +267 -18
  42. package/dist/core/operations/index.d.ts +1 -1
  43. package/dist/core/operations/index.mjs +368 -24
  44. package/dist/core/promises.mjs +4 -4
  45. package/dist/core/rpc-responses.d.ts +1 -57
  46. package/dist/core/rpc-responses.mjs +14 -15
  47. package/dist/core/shelby-explorer.mjs +31 -6
  48. package/dist/core/sp/chunk_proof.d.ts +23 -0
  49. package/dist/core/sp/chunk_proof.mjs +113 -0
  50. package/dist/core/sp/index.d.ts +3 -0
  51. package/dist/core/sp/index.mjs +402 -0
  52. package/dist/core/sp/sp_write_client.d.ts +53 -0
  53. package/dist/core/sp/sp_write_client.mjs +302 -0
  54. package/dist/core/strings.mjs +0 -2
  55. package/dist/core/types/blobs.d.ts +24 -5
  56. package/dist/core/types/blobs.mjs +24 -1
  57. package/dist/core/types/index.d.ts +2 -2
  58. package/dist/core/types/index.mjs +275 -10
  59. package/dist/core/types/payments.d.ts +6 -6
  60. package/dist/core/types/payments.mjs +230 -5
  61. package/dist/core/types/placement_groups.mjs +0 -1
  62. package/dist/core/types/storage_providers.d.ts +32 -6
  63. package/dist/core/types/storage_providers.mjs +22 -1
  64. package/dist/core/utils.mjs +94 -8
  65. package/dist/gen/rpc_server_pb.d.ts +295 -0
  66. package/dist/gen/rpc_server_pb.mjs +28 -0
  67. package/dist/node/clients/ShelbyNodeClient.d.ts +2 -0
  68. package/dist/node/clients/ShelbyNodeClient.mjs +3237 -24
  69. package/dist/node/clients/index.d.ts +2 -0
  70. package/dist/node/clients/index.mjs +3236 -26
  71. package/dist/node/index.d.ts +9 -9
  72. package/dist/node/index.mjs +4217 -153
  73. package/dist/node/parallel/commitment_worker.d.ts +9 -0
  74. package/dist/node/parallel/commitment_worker.mjs +85 -0
  75. package/dist/node/parallel/commitment_worker_pool.d.ts +35 -0
  76. package/dist/node/parallel/commitment_worker_pool.mjs +339 -0
  77. package/dist/node/parallel/coverage_flush.d.ts +16 -0
  78. package/dist/node/parallel/coverage_flush.mjs +43 -0
  79. package/dist/node/parallel/default_commitment_worker_pool.d.ts +24 -0
  80. package/dist/node/parallel/default_commitment_worker_pool.mjs +355 -0
  81. package/dist/node/parallel/index.d.ts +12 -0
  82. package/dist/node/parallel/index.mjs +631 -0
  83. package/dist/node/parallel/parallel_commitments.d.ts +32 -0
  84. package/dist/node/parallel/parallel_commitments.mjs +614 -0
  85. package/dist/node/parallel/worker_pool.d.ts +74 -0
  86. package/dist/node/parallel/worker_pool.mjs +301 -0
  87. package/dist/node/testUtil.mjs +8 -4
  88. package/package.json +16 -5
  89. package/dist/chunk-3NRBHSMQ.mjs +0 -12
  90. package/dist/chunk-5BVSWXQJ.mjs +0 -801
  91. package/dist/chunk-6FJNAUU5.mjs +0 -278
  92. package/dist/chunk-6XSZRDLJ.mjs +0 -375
  93. package/dist/chunk-7P6ASYW6.mjs +0 -9
  94. package/dist/chunk-A4IG6GSE.mjs +0 -21
  95. package/dist/chunk-AD2G3QYD.mjs +0 -0
  96. package/dist/chunk-AUQDI5BS.mjs +0 -43
  97. package/dist/chunk-AY3HBWXA.mjs +0 -51
  98. package/dist/chunk-BDSW5PHM.mjs +0 -37
  99. package/dist/chunk-C6RQ3AEU.mjs +0 -51
  100. package/dist/chunk-CQ6QPIZK.mjs +0 -37
  101. package/dist/chunk-D6GQHO6G.mjs +0 -15
  102. package/dist/chunk-EM67QTMR.mjs +0 -0
  103. package/dist/chunk-I6NG5GNL.mjs +0 -8
  104. package/dist/chunk-JTXYKO3U.mjs +0 -38
  105. package/dist/chunk-KG2TMLEV.mjs +0 -207
  106. package/dist/chunk-M3CP5TFP.mjs +0 -39
  107. package/dist/chunk-M7W43K2Q.mjs +0 -505
  108. package/dist/chunk-MB7C7VQF.mjs +0 -0
  109. package/dist/chunk-MNCQBEMS.mjs +0 -55
  110. package/dist/chunk-MQUVYMNQ.mjs +0 -0
  111. package/dist/chunk-NEO7Z3ZQ.mjs +0 -49
  112. package/dist/chunk-NHWWORCH.mjs +0 -72
  113. package/dist/chunk-OEK24ON7.mjs +0 -135
  114. package/dist/chunk-QQ57OGQ2.mjs +0 -0
  115. package/dist/chunk-RNNJXTX2.mjs +0 -154
  116. package/dist/chunk-S6Q4OG7H.mjs +0 -83
  117. package/dist/chunk-TET3DJEO.mjs +0 -199
  118. package/dist/chunk-UEZNZBJO.mjs +0 -104
  119. package/dist/chunk-V46D5NBW.mjs +0 -11
  120. package/dist/chunk-V4B3GAMQ.mjs +0 -42
  121. package/dist/chunk-WFWRBNTW.mjs +0 -412
  122. package/dist/chunk-WTICJPDB.mjs +0 -0
  123. package/dist/chunk-XNEIWM4O.mjs +0 -0
  124. package/dist/chunk-Z4FZ7W6L.mjs +0 -39
  125. package/dist/chunk-Z7RFCADT.mjs +0 -0
  126. package/dist/chunk-ZHXCVRZX.mjs +0 -0
@@ -1,16 +1,1249 @@
1
+ // src/core/clients/ShelbyRPCClient.ts
1
2
  import {
2
- ShelbyRPCClient
3
- } from "../../chunk-WFWRBNTW.mjs";
4
- import "../../chunk-KG2TMLEV.mjs";
5
- import "../../chunk-NEO7Z3ZQ.mjs";
6
- import "../../chunk-TET3DJEO.mjs";
7
- import "../../chunk-V4B3GAMQ.mjs";
8
- import "../../chunk-Z4FZ7W6L.mjs";
9
- import "../../chunk-I6NG5GNL.mjs";
10
- import "../../chunk-M3CP5TFP.mjs";
11
- import "../../chunk-UEZNZBJO.mjs";
12
- import "../../chunk-MNCQBEMS.mjs";
13
- import "../../chunk-7P6ASYW6.mjs";
3
+ Hex as Hex3
4
+ } from "@aptos-labs/ts-sdk";
5
+
6
+ // src/core/erasure/constants.ts
7
+ var ERASURE_CODE_PARAMS = {
8
+ ["ClayCode_16Total_10Data_13Helper" /* ClayCode_16Total_10Data_13Helper */]: {
9
+ // total chunks (data + parity)
10
+ erasure_n: 16,
11
+ // data chunks
12
+ erasure_k: 10,
13
+ // helper nodes
14
+ erasure_d: 13,
15
+ // enum index
16
+ enumIndex: 0
17
+ },
18
+ ["ClayCode_4Total_2Data_3Helper" /* ClayCode_4Total_2Data_3Helper */]: {
19
+ // total chunks (data + parity)
20
+ erasure_n: 4,
21
+ // data chunks
22
+ erasure_k: 2,
23
+ // helper nodes
24
+ erasure_d: 3,
25
+ // enum index
26
+ enumIndex: 1
27
+ }
28
+ };
29
+ function findErasureSchemeByErasureN(erasureN) {
30
+ return Object.entries(ERASURE_CODE_PARAMS).find(
31
+ ([, params]) => params.erasure_n === erasureN
32
+ );
33
+ }
34
+ var DEFAULT_ERASURE_N = ERASURE_CODE_PARAMS["ClayCode_16Total_10Data_13Helper" /* ClayCode_16Total_10Data_13Helper */].erasure_n;
35
+ var DEFAULT_ERASURE_K = ERASURE_CODE_PARAMS["ClayCode_16Total_10Data_13Helper" /* ClayCode_16Total_10Data_13Helper */].erasure_k;
36
+ var DEFAULT_ERASURE_D = ERASURE_CODE_PARAMS["ClayCode_16Total_10Data_13Helper" /* ClayCode_16Total_10Data_13Helper */].erasure_d;
37
+ var DEFAULT_ERASURE_M = DEFAULT_ERASURE_N - DEFAULT_ERASURE_K;
38
+
39
+ // src/core/chunk.ts
40
+ var CHUNK_SIZE_PARAMS = {
41
+ ["ChunkSet10MiB_Chunk1MiB" /* ChunkSet10MiB_Chunk1MiB */]: {
42
+ // 1MiB
43
+ chunkSizeBytes: 1 * 1024 * 1024,
44
+ // 10MiB
45
+ chunksetSizeBytes: 10 * 1024 * 1024
46
+ },
47
+ ["ChunkSet2MiB_Chunk1MiB" /* ChunkSet2MiB_Chunk1MiB */]: {
48
+ // 1MiB
49
+ chunkSizeBytes: 1 * 1024 * 1024,
50
+ // 2MiB
51
+ chunksetSizeBytes: 2 * 1024 * 1024
52
+ }
53
+ };
54
+ var DEFAULT_CHUNK_SIZE_BYTES = CHUNK_SIZE_PARAMS["ChunkSet10MiB_Chunk1MiB" /* ChunkSet10MiB_Chunk1MiB */].chunkSizeBytes;
55
+ var DEFAULT_CHUNKSET_SIZE_BYTES = CHUNK_SIZE_PARAMS["ChunkSet10MiB_Chunk1MiB" /* ChunkSet10MiB_Chunk1MiB */].chunksetSizeBytes;
56
+ var ERASURE_CODE_AND_CHUNK_MAPPING = {
57
+ ["ClayCode_16Total_10Data_13Helper" /* ClayCode_16Total_10Data_13Helper */]: {
58
+ ...CHUNK_SIZE_PARAMS.ChunkSet10MiB_Chunk1MiB
59
+ },
60
+ ["ClayCode_4Total_2Data_3Helper" /* ClayCode_4Total_2Data_3Helper */]: {
61
+ ...CHUNK_SIZE_PARAMS.ChunkSet2MiB_Chunk1MiB
62
+ }
63
+ };
64
+
65
+ // src/core/layout.ts
66
+ import { z } from "zod";
67
+ var BlobNameSchema = z.string().min(1, "Blob name path parameter cannot be empty.").max(
68
+ 190,
69
+ "Blob name suffix cannot exceed 190 characters (on-chain full key limit is 256 bytes)."
70
+ ).refine((name) => !name.endsWith("/"), {
71
+ message: "Blob name cannot end with a slash"
72
+ });
73
+
74
+ // src/core/operations/index.ts
75
+ import { Network as Network2 } from "@aptos-labs/ts-sdk";
76
+ import { GraphQLClient } from "graphql-request";
77
+
78
+ // src/core/clients/utils.ts
79
+ import { AptosConfig } from "@aptos-labs/ts-sdk";
80
+
81
+ // src/core/constants.ts
82
+ import { Network } from "@aptos-labs/ts-sdk";
83
+ var NetworkToShelbyRPCBaseUrl = {
84
+ [Network.SHELBYNET]: "https://shelby.shelbynet.shelby.xyz/shelby",
85
+ [Network.NETNA]: void 0,
86
+ [Network.DEVNET]: void 0,
87
+ [Network.TESTNET]: "https://api.testnet.shelby.xyz/shelby",
88
+ [Network.MAINNET]: void 0,
89
+ [Network.LOCAL]: "http://localhost:9090",
90
+ [Network.CUSTOM]: void 0
91
+ };
92
+ var NetworkToShelbyBlobIndexerBaseUrl = {
93
+ [Network.SHELBYNET]: "https://api.shelbynet.aptoslabs.com/nocode/v1/public/alias/shelby/shelbynet/v1/graphql",
94
+ [Network.NETNA]: void 0,
95
+ [Network.DEVNET]: void 0,
96
+ [Network.TESTNET]: "https://api.testnet.aptoslabs.com/nocode/v1/public/alias/shelby/testnet/v1/graphql",
97
+ [Network.MAINNET]: void 0,
98
+ [Network.LOCAL]: "http://localhost:8091/v1/graphql",
99
+ [Network.CUSTOM]: void 0
100
+ };
101
+ var NetworkToGasStationBaseUrl = {
102
+ [Network.SHELBYNET]: "https://api.shelbynet.shelby.xyz/gs/v1",
103
+ [Network.NETNA]: void 0,
104
+ [Network.DEVNET]: void 0,
105
+ [Network.TESTNET]: "https://api.testnet.shelby.xyz/gs/v1",
106
+ [Network.MAINNET]: void 0,
107
+ [Network.LOCAL]: void 0,
108
+ [Network.CUSTOM]: void 0
109
+ };
110
+ var MICROPAYMENTS_DEPLOYER = "0x1ae7275148bf6ef742b658fd9cbcc2e094201606f4a7bc707bab0201da8043ee";
111
+
112
+ // src/core/clients/utils.ts
113
+ var getAptosConfig = (config) => {
114
+ const baseFaucetConfig = config.faucet?.authToken ? { AUTH_TOKEN: config.faucet.authToken } : void 0;
115
+ if (config.aptos) {
116
+ return new AptosConfig({
117
+ // Spread user's aptos config first
118
+ ...config.aptos,
119
+ // Network from top-level is authoritative - cannot be overridden
120
+ network: config.network,
121
+ // Merge clientConfig with API_KEY default
122
+ clientConfig: {
123
+ API_KEY: config.apiKey,
124
+ ...config.aptos.clientConfig
125
+ },
126
+ // Merge faucetConfig: aptos config first, then base authToken (authoritative)
127
+ faucetConfig: {
128
+ ...config.aptos.faucetConfig,
129
+ ...baseFaucetConfig
130
+ }
131
+ });
132
+ }
133
+ return new AptosConfig({
134
+ network: config.network,
135
+ clientConfig: {
136
+ API_KEY: config.apiKey
137
+ },
138
+ faucetConfig: baseFaucetConfig
139
+ });
140
+ };
141
+ var getShelbyRPCBaseUrl = (config) => {
142
+ return config.rpc?.baseUrl ?? NetworkToShelbyRPCBaseUrl[config.network] ?? NetworkToShelbyRPCBaseUrl.testnet;
143
+ };
144
+
145
+ // src/core/operations/generated/sdk.ts
146
+ import gql from "graphql-tag";
147
+ var GetBlobsDocument = gql`
148
+ query getBlobs($where: blobs_bool_exp, $orderBy: [blobs_order_by!], $limit: Int, $offset: Int) {
149
+ blobs(where: $where, order_by: $orderBy, limit: $limit, offset: $offset) {
150
+ uid
151
+ object_name
152
+ owner
153
+ blob_commitment
154
+ created_at
155
+ expires_at
156
+ updated_at
157
+ num_chunksets
158
+ size
159
+ slice_address
160
+ placement_group
161
+ is_persisted
162
+ is_committed
163
+ is_deleted
164
+ }
165
+ }
166
+ `;
167
+ var GetBlobActivitiesDocument = gql`
168
+ query getBlobActivities($where: blob_activities_bool_exp, $orderBy: [blob_activities_order_by!], $limit: Int, $offset: Int) {
169
+ blob_activities(
170
+ where: $where
171
+ order_by: $orderBy
172
+ limit: $limit
173
+ offset: $offset
174
+ ) {
175
+ uid
176
+ object_name
177
+ event_index
178
+ event_type
179
+ transaction_hash
180
+ transaction_version
181
+ timestamp
182
+ owner
183
+ }
184
+ }
185
+ `;
186
+ var GetBlobsCountDocument = gql`
187
+ query getBlobsCount($where: blobs_bool_exp) {
188
+ blobs_aggregate(where: $where) {
189
+ aggregate {
190
+ count
191
+ }
192
+ }
193
+ }
194
+ `;
195
+ var GetBlobActivitiesCountDocument = gql`
196
+ query getBlobActivitiesCount($where: blob_activities_bool_exp) {
197
+ blob_activities_aggregate(where: $where) {
198
+ aggregate {
199
+ count
200
+ }
201
+ }
202
+ }
203
+ `;
204
+ var GetTotalBlobsSizeDocument = gql`
205
+ query getTotalBlobsSize($where: blobs_bool_exp) {
206
+ blobs_aggregate(where: $where) {
207
+ aggregate {
208
+ sum {
209
+ size
210
+ }
211
+ }
212
+ }
213
+ }
214
+ `;
215
+ var GetPlacementGroupSlotsDocument = gql`
216
+ query getPlacementGroupSlots($where: placement_group_slots_bool_exp, $orderBy: [placement_group_slots_order_by!], $limit: Int, $offset: Int) {
217
+ placement_group_slots(
218
+ where: $where
219
+ order_by: $orderBy
220
+ limit: $limit
221
+ offset: $offset
222
+ ) {
223
+ placement_group
224
+ slot_index
225
+ storage_provider
226
+ status
227
+ updated_at
228
+ }
229
+ }
230
+ `;
231
+ var GetPlacementGroupSlotsCountDocument = gql`
232
+ query getPlacementGroupSlotsCount($where: placement_group_slots_bool_exp) {
233
+ placement_group_slots_aggregate(where: $where) {
234
+ aggregate {
235
+ count
236
+ }
237
+ }
238
+ }
239
+ `;
240
+ var GetProcessorStatusDocument = gql`
241
+ query getProcessorStatus {
242
+ processor_status {
243
+ last_success_version
244
+ last_transaction_timestamp
245
+ last_updated
246
+ }
247
+ }
248
+ `;
249
+ var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
250
+ function getSdk(client, withWrapper = defaultWrapper) {
251
+ return {
252
+ getBlobs(variables, requestHeaders, signal) {
253
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetBlobsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getBlobs", "query", variables);
254
+ },
255
+ getBlobActivities(variables, requestHeaders, signal) {
256
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetBlobActivitiesDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getBlobActivities", "query", variables);
257
+ },
258
+ getBlobsCount(variables, requestHeaders, signal) {
259
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetBlobsCountDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getBlobsCount", "query", variables);
260
+ },
261
+ getBlobActivitiesCount(variables, requestHeaders, signal) {
262
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetBlobActivitiesCountDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getBlobActivitiesCount", "query", variables);
263
+ },
264
+ getTotalBlobsSize(variables, requestHeaders, signal) {
265
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetTotalBlobsSizeDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getTotalBlobsSize", "query", variables);
266
+ },
267
+ getPlacementGroupSlots(variables, requestHeaders, signal) {
268
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetPlacementGroupSlotsDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getPlacementGroupSlots", "query", variables);
269
+ },
270
+ getPlacementGroupSlotsCount(variables, requestHeaders, signal) {
271
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetPlacementGroupSlotsCountDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getPlacementGroupSlotsCount", "query", variables);
272
+ },
273
+ getProcessorStatus(variables, requestHeaders, signal) {
274
+ return withWrapper((wrappedRequestHeaders) => client.request({ document: GetProcessorStatusDocument, variables, requestHeaders: { ...requestHeaders, ...wrappedRequestHeaders }, signal }), "getProcessorStatus", "query", variables);
275
+ }
276
+ };
277
+ }
278
+
279
+ // src/core/operations/index.ts
280
+ function createShelbyIndexerClient(baseUrl, options) {
281
+ const graphqlClient = new GraphQLClient(baseUrl, options);
282
+ return getSdk(graphqlClient);
283
+ }
284
+ function getShelbyIndexerClient(config) {
285
+ const aptosConfig = getAptosConfig(config);
286
+ let { apiKey: indexerApiKey, baseUrl } = config.indexer ?? {};
287
+ if (!baseUrl) {
288
+ switch (aptosConfig?.network) {
289
+ // TODO: Add endpoints for core networks
290
+ case Network2.MAINNET:
291
+ case Network2.TESTNET:
292
+ case Network2.DEVNET:
293
+ case Network2.SHELBYNET:
294
+ case Network2.LOCAL:
295
+ baseUrl = NetworkToShelbyBlobIndexerBaseUrl[aptosConfig.network];
296
+ break;
297
+ }
298
+ }
299
+ if (baseUrl === void 0) {
300
+ throw new Error(
301
+ "The ShelbyClientConfig does not support the Shelby indexer. Please configure the endpoint in the ShelbyClientConfig.indexer.endpoint or provide a supported Aptos network."
302
+ );
303
+ }
304
+ const apiKey = indexerApiKey ?? config.apiKey;
305
+ return createShelbyIndexerClient(baseUrl, {
306
+ headers: {
307
+ ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
308
+ "x-aptos-client": "shelby-ts-sdk"
309
+ }
310
+ });
311
+ }
312
+
313
+ // src/core/promises.ts
314
+ function sleep(ms) {
315
+ return new Promise((resolve) => setTimeout(resolve, ms));
316
+ }
317
+
318
+ // src/core/rpc-responses.ts
319
+ import { z as z2 } from "zod";
320
+ var ChallengeResponseSchema = z2.object({
321
+ challenge: z2.string(),
322
+ expiresAt: z2.number()
323
+ });
324
+ var RPCErrorResponseSchema = z2.object({
325
+ error: z2.string()
326
+ });
327
+ var StaleMicropaymentErrorResponseSchema = z2.object({
328
+ error: z2.string().optional(),
329
+ storedMicropayment: z2.string().optional()
330
+ });
331
+
332
+ // src/core/types/payments.ts
333
+ import {
334
+ AccountAddress,
335
+ Deserializer,
336
+ Hex,
337
+ Serializer
338
+ } from "@aptos-labs/ts-sdk";
339
+ var MICROPAYMENTS_MODULE_NAME = "micropayments";
340
+ var WITHDRAW_APPROVAL_STRUCT_NAME = "WithdrawApproval";
341
+ function serializeTypeInfo(serializer, moduleAddress, moduleName, structName) {
342
+ moduleAddress.serialize(serializer);
343
+ const moduleNameBytes = new TextEncoder().encode(moduleName);
344
+ serializer.serializeBytes(moduleNameBytes);
345
+ const structNameBytes = new TextEncoder().encode(structName);
346
+ serializer.serializeBytes(structNameBytes);
347
+ }
348
+ var StaleChannelStateError = class _StaleChannelStateError extends Error {
349
+ /**
350
+ * The last valid micropayment stored by the server.
351
+ * Clients can use this to reset their local channel state.
352
+ */
353
+ storedMicropayment;
354
+ constructor(storedMicropayment, message) {
355
+ super(
356
+ message ?? "Client has stale channel state. Use the returned micropayment to reset local state."
357
+ );
358
+ this.name = "StaleChannelStateError";
359
+ this.storedMicropayment = storedMicropayment;
360
+ }
361
+ /**
362
+ * Returns the stored micropayment as a base64-encoded string.
363
+ */
364
+ toBase64() {
365
+ const bytes = this.storedMicropayment.bcsToBytes();
366
+ const binaryString = Array.from(
367
+ bytes,
368
+ (byte) => String.fromCharCode(byte)
369
+ ).join("");
370
+ return btoa(binaryString);
371
+ }
372
+ /**
373
+ * Creates a StaleChannelStateError from a base64-encoded micropayment string.
374
+ */
375
+ static fromBase64(base64, message) {
376
+ const binaryString = atob(base64);
377
+ const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
378
+ const micropayment = SenderBuiltMicropayment.deserialize(bytes);
379
+ return new _StaleChannelStateError(micropayment, message);
380
+ }
381
+ };
382
+ var SenderBuiltMicropayment = class _SenderBuiltMicropayment {
383
+ /**
384
+ * The sender's address (owner of the payment channel).
385
+ */
386
+ sender;
387
+ /**
388
+ * The receiver's address (beneficiary of the withdrawal).
389
+ */
390
+ receiver;
391
+ /**
392
+ * The unique ID of the payment channel.
393
+ */
394
+ paymentChannelId;
395
+ /**
396
+ * The cumulative amount the receiver is authorized to withdraw.
397
+ */
398
+ amount;
399
+ /**
400
+ * The fungible asset metadata address.
401
+ */
402
+ fungibleAssetAddress;
403
+ /**
404
+ * Monotonically increasing sequence number for replay protection.
405
+ */
406
+ sequenceNumber;
407
+ /**
408
+ * The sender's Ed25519 public key (32 bytes).
409
+ * Used by the receiver to verify the signature before submitting.
410
+ */
411
+ publicKey;
412
+ /**
413
+ * The Ed25519 signature of the SignedMessage<WithdrawApproval> struct.
414
+ * The SignedMessage includes TypeInfo for domain separation.
415
+ */
416
+ signature;
417
+ /**
418
+ * The deployer address of the micropayments module.
419
+ * This is needed to reconstruct the TypeInfo for signature verification.
420
+ */
421
+ deployer;
422
+ constructor(sender, receiver, paymentChannelId, amount, fungibleAssetAddress, sequenceNumber, publicKey, signature, deployer) {
423
+ this.sender = sender;
424
+ this.receiver = receiver;
425
+ this.paymentChannelId = paymentChannelId;
426
+ this.amount = amount;
427
+ this.fungibleAssetAddress = fungibleAssetAddress;
428
+ this.sequenceNumber = sequenceNumber;
429
+ this.publicKey = publicKey;
430
+ this.signature = signature;
431
+ this.deployer = deployer ?? AccountAddress.fromString(MICROPAYMENTS_DEPLOYER);
432
+ }
433
+ /**
434
+ * Creates the BCS-serialized message that was signed.
435
+ * This is a SignedMessage<WithdrawApproval> which includes:
436
+ * 1. TypeInfo (module_address, module_name, struct_name)
437
+ * 2. WithdrawApproval struct fields
438
+ *
439
+ * This format is used with signature_verify_strict_t for domain separation.
440
+ */
441
+ getSignedMessage() {
442
+ return _SenderBuiltMicropayment.buildSignedMessage({
443
+ deployer: this.deployer,
444
+ sender: this.sender,
445
+ receiver: this.receiver,
446
+ fungibleAssetAddress: this.fungibleAssetAddress,
447
+ amount: this.amount,
448
+ paymentChannelId: this.paymentChannelId,
449
+ sequenceNumber: this.sequenceNumber
450
+ });
451
+ }
452
+ /**
453
+ * Static helper to build the SignedMessage<WithdrawApproval> bytes from raw parameters.
454
+ * This can be used to create the message for signing without constructing the full object.
455
+ *
456
+ * @param params - The withdrawal approval parameters.
457
+ * @returns The BCS-serialized SignedMessage<WithdrawApproval> bytes.
458
+ */
459
+ static buildSignedMessage(params) {
460
+ const serializer = new Serializer();
461
+ serializeTypeInfo(
462
+ serializer,
463
+ params.deployer,
464
+ MICROPAYMENTS_MODULE_NAME,
465
+ WITHDRAW_APPROVAL_STRUCT_NAME
466
+ );
467
+ params.sender.serialize(serializer);
468
+ params.receiver.serialize(serializer);
469
+ params.fungibleAssetAddress.serialize(serializer);
470
+ serializer.serializeU64(params.amount);
471
+ serializer.serializeU64(params.paymentChannelId);
472
+ serializer.serializeU64(params.sequenceNumber);
473
+ return serializer.toUint8Array();
474
+ }
475
+ serialize(serializer) {
476
+ this.sender.serialize(serializer);
477
+ this.receiver.serialize(serializer);
478
+ this.fungibleAssetAddress.serialize(serializer);
479
+ serializer.serializeU64(this.amount);
480
+ serializer.serializeU64(this.paymentChannelId);
481
+ serializer.serializeU64(this.sequenceNumber);
482
+ serializer.serializeBytes(this.publicKey);
483
+ serializer.serializeBytes(this.signature);
484
+ this.deployer.serialize(serializer);
485
+ }
486
+ bcsToBytes() {
487
+ const serializer = new Serializer();
488
+ this.serialize(serializer);
489
+ return serializer.toUint8Array();
490
+ }
491
+ bcsToHex() {
492
+ return Hex.fromHexInput(this.bcsToBytes());
493
+ }
494
+ toStringWithoutPrefix() {
495
+ return this.bcsToHex().toStringWithoutPrefix();
496
+ }
497
+ toString() {
498
+ return this.bcsToHex().toString();
499
+ }
500
+ /**
501
+ * Deserializes a SenderBuiltMicropayment from BCS bytes.
502
+ * @param bytes - The bytes to deserialize from (Uint8Array or hex string).
503
+ * @returns A new SenderBuiltMicropayment instance.
504
+ */
505
+ static deserialize(bytes) {
506
+ const bytesArray = typeof bytes === "string" ? Hex.fromHexInput(bytes).toUint8Array() : bytes;
507
+ const deserializer = new Deserializer(bytesArray);
508
+ const sender = AccountAddress.deserialize(deserializer);
509
+ const receiver = AccountAddress.deserialize(deserializer);
510
+ const fungibleAssetAddress = AccountAddress.deserialize(deserializer);
511
+ const amount = deserializer.deserializeU64();
512
+ const paymentChannelId = deserializer.deserializeU64();
513
+ const sequenceNumber = deserializer.deserializeU64();
514
+ const publicKey = deserializer.deserializeBytes();
515
+ const signature = deserializer.deserializeBytes();
516
+ const deployer = AccountAddress.deserialize(deserializer);
517
+ return new _SenderBuiltMicropayment(
518
+ sender,
519
+ receiver,
520
+ paymentChannelId,
521
+ amount,
522
+ fungibleAssetAddress,
523
+ sequenceNumber,
524
+ publicKey,
525
+ signature,
526
+ deployer
527
+ );
528
+ }
529
+ };
530
+
531
+ // src/core/utils.ts
532
+ import {
533
+ AccountAddress as AccountAddress2,
534
+ Hex as Hex2
535
+ } from "@aptos-labs/ts-sdk";
536
+ async function* readInChunks(input, chunkSize) {
537
+ let idx = 0;
538
+ if (isReadableStream(input)) {
539
+ const reader = input.getReader();
540
+ let buffer = new Uint8Array(chunkSize);
541
+ let bufferWriteOffset = 0;
542
+ try {
543
+ while (true) {
544
+ const { value, done } = await reader.read();
545
+ if (done) break;
546
+ if (value === void 0) continue;
547
+ let srcOffset = 0;
548
+ while (srcOffset < value.length) {
549
+ const remainingCapacity = chunkSize - bufferWriteOffset;
550
+ const bytesToCopy = Math.min(
551
+ remainingCapacity,
552
+ value.length - srcOffset
553
+ );
554
+ buffer.set(
555
+ value.subarray(srcOffset, srcOffset + bytesToCopy),
556
+ bufferWriteOffset
557
+ );
558
+ bufferWriteOffset += bytesToCopy;
559
+ srcOffset += bytesToCopy;
560
+ if (bufferWriteOffset >= chunkSize) {
561
+ yield [idx++, buffer];
562
+ buffer = new Uint8Array(chunkSize);
563
+ bufferWriteOffset = 0;
564
+ }
565
+ }
566
+ }
567
+ } finally {
568
+ reader.releaseLock();
569
+ }
570
+ if (bufferWriteOffset > 0) {
571
+ yield [idx++, buffer.subarray(0, bufferWriteOffset)];
572
+ }
573
+ return;
574
+ }
575
+ const view = toUint8Array(input);
576
+ for (let offset = 0; offset < view.byteLength; offset += chunkSize) {
577
+ yield [
578
+ idx++,
579
+ view.subarray(offset, Math.min(offset + chunkSize, view.byteLength))
580
+ ];
581
+ }
582
+ }
583
+ async function concatHashes(parts) {
584
+ const chunks = parts.map((part) => Hex2.fromHexInput(part).toUint8Array());
585
+ const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
586
+ const combined = new Uint8Array(totalLength);
587
+ let offset = 0;
588
+ for (const chunk of chunks) {
589
+ combined.set(chunk, offset);
590
+ offset += chunk.byteLength;
591
+ }
592
+ return Hex2.fromHexInput(
593
+ new Uint8Array(await crypto.subtle.digest("SHA-256", combined))
594
+ );
595
+ }
596
+ function isReadableStream(value) {
597
+ return typeof value === "object" && value !== null && "getReader" in value && typeof value.getReader === "function";
598
+ }
599
+ function toUint8Array(view) {
600
+ return view instanceof Uint8Array ? view : new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
601
+ }
602
+ function buildRequestUrl(path, baseUrl) {
603
+ const baseHasSlash = baseUrl.endsWith("/");
604
+ const safeBase = baseHasSlash ? baseUrl : `${baseUrl}/`;
605
+ const safePath = path.replace(/^\/+/, "");
606
+ return new URL(safePath, safeBase);
607
+ }
608
+ function normalizeAddress(address) {
609
+ return AccountAddress2.from(address, { maxMissingChars: 63 });
610
+ }
611
+
612
+ // src/core/clients/ShelbyRPCClient.ts
613
+ var MICROPAYMENT_HEADER = "X-Shelby-Micropayment";
614
+ var BLOB_OWNER_CHALLENGE_HEADER = "X-Shelby-Challenge";
615
+ var BLOB_OWNER_SIGNATURE_HEADER = "X-Shelby-Signature";
616
+ var BLOB_OWNER_PUBLIC_KEY_HEADER = "X-Shelby-Public-Key";
617
+ var BLOB_OWNER_AUTH_SCHEME_HEADER = "X-Shelby-Auth-Scheme";
618
+ var BLOB_OWNER_IDENTITY_HEADER = "X-Shelby-Identity";
619
+ var BLOB_OWNER_DOMAIN_HEADER = "X-Shelby-Domain";
620
+ var BLOB_OWNER_AUTH_FUNCTION_HEADER = "X-Shelby-Auth-Function";
621
+ var INCLUSION_PROOF_HEADER = "X-Shelby-Inclusion-Proof";
622
+ function buildAuthHeaders(auth) {
623
+ const signatureBase64 = btoa(
624
+ Array.from(auth.signature, (byte) => String.fromCharCode(byte)).join("")
625
+ );
626
+ const publicKeyHex = Hex3.fromHexInput(auth.publicKey).toString();
627
+ const headers = {
628
+ [BLOB_OWNER_CHALLENGE_HEADER]: auth.challenge,
629
+ [BLOB_OWNER_SIGNATURE_HEADER]: signatureBase64,
630
+ [BLOB_OWNER_PUBLIC_KEY_HEADER]: publicKeyHex
631
+ };
632
+ if (auth.authScheme === "derivable") {
633
+ headers[BLOB_OWNER_AUTH_SCHEME_HEADER] = auth.authScheme;
634
+ headers[BLOB_OWNER_IDENTITY_HEADER] = auth.identity;
635
+ headers[BLOB_OWNER_DOMAIN_HEADER] = auth.domain;
636
+ headers[BLOB_OWNER_AUTH_FUNCTION_HEADER] = auth.authFunction;
637
+ } else if (auth.authScheme) {
638
+ headers[BLOB_OWNER_AUTH_SCHEME_HEADER] = auth.authScheme;
639
+ }
640
+ return headers;
641
+ }
642
+ function encodeURIComponentKeepSlashes(str) {
643
+ return encodeURIComponent(str).replace(/%2F/g, "/");
644
+ }
645
+ async function generateChunksetInclusionProof(chunksetRoots, chunksetIndex) {
646
+ if (chunksetRoots.length === 0) {
647
+ throw new Error("Cannot generate inclusion proof for empty chunkset roots");
648
+ }
649
+ if (chunksetIndex < 0 || chunksetIndex >= chunksetRoots.length) {
650
+ throw new Error(
651
+ `Chunkset index ${chunksetIndex} out of range [0, ${chunksetRoots.length})`
652
+ );
653
+ }
654
+ if (chunksetRoots.length === 1) {
655
+ return [];
656
+ }
657
+ const zeroHash = new Uint8Array(32);
658
+ const siblings = [];
659
+ let currentLeaves = chunksetRoots.map((h) => h.toUint8Array());
660
+ let currentIdx = chunksetIndex;
661
+ while (currentLeaves.length > 1) {
662
+ if (currentLeaves.length % 2 !== 0) {
663
+ currentLeaves.push(zeroHash);
664
+ }
665
+ const siblingIdx = currentIdx % 2 === 0 ? currentIdx + 1 : currentIdx - 1;
666
+ siblings.push(currentLeaves[siblingIdx]);
667
+ const nextLeaves = [];
668
+ for (let i = 0; i < currentLeaves.length; i += 2) {
669
+ const combined = await concatHashes([
670
+ currentLeaves[i],
671
+ currentLeaves[i + 1]
672
+ ]);
673
+ nextLeaves.push(combined.toUint8Array());
674
+ }
675
+ currentLeaves = nextLeaves;
676
+ currentIdx = Math.floor(currentIdx / 2);
677
+ }
678
+ return siblings;
679
+ }
680
+ function encodeInclusionProof(siblings) {
681
+ if (siblings.length === 0) {
682
+ return "NONE";
683
+ }
684
+ const totalBytes = siblings.length * 32;
685
+ const combined = new Uint8Array(totalBytes);
686
+ let offset = 0;
687
+ for (const sibling of siblings) {
688
+ combined.set(sibling, offset);
689
+ offset += 32;
690
+ }
691
+ const binaryString = Array.from(
692
+ combined,
693
+ (byte) => String.fromCharCode(byte)
694
+ ).join("");
695
+ return btoa(binaryString);
696
+ }
697
+ function validateTotalBytes(totalBytes) {
698
+ if (!Number.isInteger(totalBytes) || totalBytes < 0) {
699
+ throw new Error("totalBytes must be a non-negative integer");
700
+ }
701
+ }
702
+ function isRetryableStatus(status) {
703
+ return status === 408 || status === 429 || status >= 500;
704
+ }
705
+ function getErrorCode(error) {
706
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
707
+ return error.code;
708
+ }
709
+ if (error instanceof Error) {
710
+ const match = error.message.match(/\bE[A-Z0-9_]+\b/);
711
+ return match?.[0];
712
+ }
713
+ return void 0;
714
+ }
715
+ var ShelbyRPCClient = class {
716
+ baseUrl;
717
+ apiKey;
718
+ rpcConfig;
719
+ indexer;
720
+ #signChallengeOverride;
721
+ /**
722
+ * Creates a new ShelbyRPCClient for interacting with Shelby RPC nodes.
723
+ * This client handles blob storage operations including upload and download.
724
+ *
725
+ * @param config - The client configuration object.
726
+ * @param config.network - The Shelby network to use.
727
+ * @param options.signChallengeHandler - Optional override for challenge
728
+ * signing. When set, `putBlobChunksets` uses this instead of the built-in
729
+ * `signChallenge`. Intended for kit-level overrides (e.g. Solana DAA).
730
+ *
731
+ * @example
732
+ * ```typescript
733
+ * const client = new ShelbyRPCClient({
734
+ * network: Network.SHELBYNET,
735
+ * apiKey: "AG-***",
736
+ * });
737
+ * ```
738
+ */
739
+ constructor(config, options) {
740
+ this.baseUrl = getShelbyRPCBaseUrl(config);
741
+ this.apiKey = config.apiKey ?? config.rpc?.apiKey;
742
+ this.rpcConfig = config.rpc ?? {};
743
+ this.indexer = getShelbyIndexerClient(config);
744
+ this.#signChallengeOverride = options?.signChallengeHandler;
745
+ }
746
+ /**
747
+ * Request an authentication challenge for the given account.
748
+ * The challenge must be signed and included in subsequent authenticated requests.
749
+ *
750
+ * @param account - The Aptos account address to authenticate as.
751
+ * @returns The challenge string and expiration timestamp.
752
+ *
753
+ * @example
754
+ * ```typescript
755
+ * const { challenge, expiresAt } = await client.getChallenge(account.accountAddress);
756
+ * const auth = client.signChallenge(account, challenge);
757
+ * ```
758
+ */
759
+ async getChallenge(account) {
760
+ const response = await fetch(
761
+ buildRequestUrl("/v1/auth/challenge", this.baseUrl),
762
+ {
763
+ method: "POST",
764
+ headers: {
765
+ "Content-Type": "application/json",
766
+ ...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}
767
+ },
768
+ body: JSON.stringify({ account: account.toString() })
769
+ }
770
+ );
771
+ if (!response.ok) {
772
+ const errorBody = await response.text().catch(() => "");
773
+ throw new Error(
774
+ `Failed to get challenge: status ${response.status}, body: ${errorBody}`
775
+ );
776
+ }
777
+ return ChallengeResponseSchema.parse(await response.json());
778
+ }
779
+ /**
780
+ * Sign a challenge using the given account and return auth credentials.
781
+ *
782
+ * @param account - The Aptos account to sign with.
783
+ * @param challenge - The hex-encoded challenge string from getChallenge().
784
+ * @returns BlobOwnerAuth credentials for authenticated requests.
785
+ */
786
+ signChallenge(account, challenge) {
787
+ const challengeBytes = Hex3.fromHexInput(challenge).toUint8Array();
788
+ const signature = account.sign(challengeBytes);
789
+ return {
790
+ challenge,
791
+ signature: signature.toUint8Array(),
792
+ publicKey: account.publicKey.toUint8Array()
793
+ };
794
+ }
795
+ /**
796
+ * Upload a single chunkset via the v2 chunkset API.
797
+ */
798
+ async #uploadChunkset(params) {
799
+ const maxNonBusyRetries = 5;
800
+ let lastResponse;
801
+ let lastError;
802
+ let attempts = 0;
803
+ let consecutive429s = 0;
804
+ let nonBusyRetries = 0;
805
+ const chunksetUrl = buildRequestUrl(
806
+ `/v2/chunksets/${params.account}/${params.chunksetIndex}/${params.uid}`,
807
+ this.baseUrl
808
+ );
809
+ const authHeaders = buildAuthHeaders(params.auth);
810
+ while (true) {
811
+ if (params.signal?.aborted) {
812
+ throw new DOMException("Upload aborted", "AbortError");
813
+ }
814
+ attempts++;
815
+ try {
816
+ lastResponse = await fetch(chunksetUrl, {
817
+ method: "PUT",
818
+ headers: {
819
+ "Content-Type": "application/octet-stream",
820
+ ...this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {},
821
+ ...authHeaders,
822
+ [INCLUSION_PROOF_HEADER]: params.inclusionProof
823
+ },
824
+ body: params.chunksetData,
825
+ signal: params.signal
826
+ });
827
+ lastError = void 0;
828
+ } catch (error) {
829
+ if (error instanceof DOMException && error.name === "AbortError") {
830
+ throw error;
831
+ }
832
+ lastError = error;
833
+ consecutive429s = 0;
834
+ nonBusyRetries++;
835
+ if (nonBusyRetries < maxNonBusyRetries) {
836
+ const delay2 = 2 ** nonBusyRetries * 100;
837
+ await sleep(delay2);
838
+ continue;
839
+ }
840
+ break;
841
+ }
842
+ if (lastResponse.ok) {
843
+ const json = await lastResponse.json();
844
+ const spAcks = json.spAcks?.map((ack) => ({
845
+ slot: ack.slot,
846
+ signature: Uint8Array.from(
847
+ atob(ack.signature),
848
+ (c) => c.charCodeAt(0)
849
+ )
850
+ }));
851
+ return {
852
+ success: json.success,
853
+ acksReceived: json.acksReceived,
854
+ spAcks
855
+ };
856
+ }
857
+ if (lastResponse.status === 429) {
858
+ consecutive429s++;
859
+ const baseDelay = 500;
860
+ const jitter = Math.random() * 200;
861
+ const delay2 = Math.min(
862
+ 3e4,
863
+ 2 ** consecutive429s * baseDelay + jitter
864
+ );
865
+ await sleep(delay2);
866
+ continue;
867
+ }
868
+ consecutive429s = 0;
869
+ if (!isRetryableStatus(lastResponse.status)) {
870
+ break;
871
+ }
872
+ nonBusyRetries++;
873
+ if (nonBusyRetries >= maxNonBusyRetries) {
874
+ break;
875
+ }
876
+ const delay = 2 ** nonBusyRetries * 100;
877
+ await sleep(delay);
878
+ }
879
+ if (lastError !== void 0) {
880
+ const errorCode = getErrorCode(lastError);
881
+ const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);
882
+ throw new Error(
883
+ `Failed to upload chunkset ${params.chunksetIndex} after ${attempts} attempt${attempts === 1 ? "" : "s"}. ${errorCode ? `(${errorCode}) ` : ""}Last error: ${errorMessage}`,
884
+ { cause: lastError }
885
+ );
886
+ }
887
+ const errorBody = await lastResponse?.text().catch(() => "");
888
+ throw new Error(
889
+ `Failed to upload chunkset ${params.chunksetIndex} after ${attempts} attempt${attempts === 1 ? "" : "s"}. status: ${lastResponse?.status}, body: ${errorBody}`
890
+ );
891
+ }
892
+ /**
893
+ * Uploads blob data to the Shelby RPC node using the v2 chunkset API.
894
+ * This method authenticates using challenge-response and uploads chunksets
895
+ * directly to storage providers via the RPC's worker pool.
896
+ *
897
+ * This method:
898
+ * - Sends raw chunkset data directly to the RPC for erasure encoding
899
+ * - Requires pre-computed blob commitments to generate inclusion proofs
900
+ * - Does not support resume (each chunkset is idempotent on the SP side)
901
+ *
902
+ * @param params.account - The Aptos Account (with signing capability) that owns the blob.
903
+ * @param params.uid - The blob's on-chain UID (from `BlobRegisteredEvent` at registration).
904
+ * @param params.blobData - The raw blob data as a Uint8Array or ReadableStream.
905
+ * @param params.commitments - Pre-computed blob commitments (from generateCommitments).
906
+ * @param params.totalBytes - Total byte length. Required for streams; optional for Uint8Array.
907
+ * @param params.chunksetConcurrency - Number of chunksets to upload in parallel. Defaults to 4.
908
+ * @param params.onProgress - Optional callback for upload progress.
909
+ * @param params.signal - Optional AbortSignal for cancellation. When aborted, in-flight
910
+ * HTTP requests are cancelled and an AbortError is thrown.
911
+ *
912
+ * @example
913
+ * ```typescript
914
+ * // First, generate commitments for the blob
915
+ * const commitments = await generateCommitments(provider, fileData);
916
+ *
917
+ * // Register the blob on chain, then read its UID from the register tx's
918
+ * // BlobRegisteredEvent (see ShelbyBlobClient.registeredBlobUids).
919
+ *
920
+ * // Upload using chunkset API
921
+ * await rpcClient.putBlobChunksets({
922
+ * account: myAccount,
923
+ * uid: blobUid,
924
+ * blobData: fileData,
925
+ * commitments,
926
+ * chunksetConcurrency: 8,
927
+ * });
928
+ * ```
929
+ */
930
+ async putBlobChunksets(params) {
931
+ let totalBytes;
932
+ if (params.blobData instanceof Uint8Array) {
933
+ totalBytes = params.totalBytes ?? params.blobData.length;
934
+ if (totalBytes !== params.blobData.length) {
935
+ throw new Error(
936
+ "totalBytes must match blobData.length when blobData is a Uint8Array"
937
+ );
938
+ }
939
+ } else {
940
+ if (params.totalBytes === void 0) {
941
+ throw new Error(
942
+ "totalBytes is required when blobData is a ReadableStream"
943
+ );
944
+ }
945
+ totalBytes = params.totalBytes;
946
+ }
947
+ validateTotalBytes(totalBytes);
948
+ const chunksetConcurrency = params.chunksetConcurrency ?? 4;
949
+ const totalChunksets = params.commitments.chunkset_commitments.length;
950
+ const rawDataSizeFromCommitments = params.commitments.raw_data_size;
951
+ if (rawDataSizeFromCommitments !== totalBytes) {
952
+ throw new Error(
953
+ `Data size mismatch: commitments were generated for ${rawDataSizeFromCommitments} bytes, but totalBytes=${totalBytes}. This will cause inclusion proof verification to fail.`
954
+ );
955
+ }
956
+ const chunksetRoots = params.commitments.chunkset_commitments.map(
957
+ (c) => Hex3.fromHexString(c.chunkset_root)
958
+ );
959
+ const { challenge } = await this.getChallenge(
960
+ params.account.accountAddress
961
+ );
962
+ const signFn = this.#signChallengeOverride ?? this.signChallenge.bind(this);
963
+ const auth = signFn(params.account, challenge);
964
+ const firstChunkset = params.commitments.chunkset_commitments[0];
965
+ if (!firstChunkset) {
966
+ throw new Error("Commitments must have at least one chunkset");
967
+ }
968
+ const erasure_n = firstChunkset.chunk_commitments.length;
969
+ const matchingEntry = findErasureSchemeByErasureN(erasure_n);
970
+ if (!matchingEntry) {
971
+ throw new Error(
972
+ `Unknown erasure coding scheme with erasure_n=${erasure_n}`
973
+ );
974
+ }
975
+ const [schemeKey, matchingParams] = matchingEntry;
976
+ const erasure_k = matchingParams.erasure_k;
977
+ const { chunkSizeBytes } = ERASURE_CODE_AND_CHUNK_MAPPING[schemeKey];
978
+ const chunksetSize = erasure_k * chunkSizeBytes;
979
+ const expectedChunksetCount = totalBytes === 0 ? 1 : Math.ceil(totalBytes / chunksetSize);
980
+ if (expectedChunksetCount !== totalChunksets) {
981
+ throw new Error(
982
+ `Chunkset count mismatch: expected ${expectedChunksetCount} chunksets for ${totalBytes} bytes with chunksetSize=${chunksetSize}, but commitments have ${totalChunksets} chunksets. This suggests the commitments were generated with a different encoding scheme.`
983
+ );
984
+ }
985
+ let uploadedBytes = 0;
986
+ let bytesReadFromSource = 0;
987
+ const inFlight = /* @__PURE__ */ new Set();
988
+ async function* chunksetsFromBlob() {
989
+ if (totalBytes === 0) {
990
+ yield [0, new Uint8Array(chunksetSize)];
991
+ return;
992
+ }
993
+ yield* readInChunks(params.blobData, chunksetSize);
994
+ }
995
+ const chunksetIterator = chunksetsFromBlob();
996
+ const aggregatedAcks = /* @__PURE__ */ new Map();
997
+ const internalAbort = new AbortController();
998
+ if (params.signal?.aborted) {
999
+ internalAbort.abort();
1000
+ } else if (params.signal) {
1001
+ params.signal.addEventListener("abort", () => internalAbort.abort(), {
1002
+ once: true
1003
+ });
1004
+ }
1005
+ let iteratorDone = false;
1006
+ let firstError = null;
1007
+ while (!iteratorDone || inFlight.size > 0) {
1008
+ if (internalAbort.signal.aborted) {
1009
+ throw firstError ?? new DOMException("Upload aborted", "AbortError");
1010
+ }
1011
+ while (inFlight.size < chunksetConcurrency && !iteratorDone) {
1012
+ const { value, done } = await chunksetIterator.next();
1013
+ if (done) {
1014
+ iteratorDone = true;
1015
+ break;
1016
+ }
1017
+ const [chunksetIdx, chunksetData] = value;
1018
+ bytesReadFromSource += totalBytes === 0 ? 0 : chunksetData.byteLength;
1019
+ const expectedCommitment = params.commitments.chunkset_commitments[chunksetIdx];
1020
+ if (!expectedCommitment) {
1021
+ throw new Error(
1022
+ `Chunkset index ${chunksetIdx} out of range. Commitments only have ${totalChunksets} chunksets. This suggests a chunkset size mismatch between commitment generation and upload.`
1023
+ );
1024
+ }
1025
+ const proofSiblings = await generateChunksetInclusionProof(
1026
+ chunksetRoots,
1027
+ chunksetIdx
1028
+ );
1029
+ const inclusionProof = encodeInclusionProof(proofSiblings);
1030
+ const chunksetDataCopy = new Uint8Array(chunksetData);
1031
+ const chunksetBytes = chunksetDataCopy.length;
1032
+ const uploadPromise = (async () => {
1033
+ const result = await this.#uploadChunkset({
1034
+ account: params.account.accountAddress.toString(),
1035
+ uid: params.uid,
1036
+ chunksetIndex: chunksetIdx,
1037
+ inclusionProof,
1038
+ chunksetData: chunksetDataCopy,
1039
+ auth,
1040
+ signal: internalAbort.signal
1041
+ });
1042
+ const chunksetSpAcks = result.spAcks?.map((ack) => ({
1043
+ slot: ack.slot,
1044
+ signature: ack.signature
1045
+ }));
1046
+ if (chunksetSpAcks) {
1047
+ for (const ack of chunksetSpAcks) {
1048
+ aggregatedAcks.set(ack.slot, ack.signature);
1049
+ }
1050
+ }
1051
+ uploadedBytes += chunksetBytes;
1052
+ params.onProgress?.({
1053
+ phase: "uploading",
1054
+ chunksetIdx,
1055
+ totalChunksets,
1056
+ chunksetBytes,
1057
+ uploadedBytes,
1058
+ totalBytes,
1059
+ acksReceived: result.acksReceived,
1060
+ spAcks: chunksetSpAcks
1061
+ });
1062
+ })().catch((err) => {
1063
+ if (!firstError) {
1064
+ firstError = err;
1065
+ internalAbort.abort();
1066
+ }
1067
+ }).finally(() => {
1068
+ inFlight.delete(uploadPromise);
1069
+ });
1070
+ inFlight.add(uploadPromise);
1071
+ }
1072
+ if (inFlight.size > 0) {
1073
+ await Promise.race(inFlight);
1074
+ }
1075
+ }
1076
+ if (firstError) {
1077
+ throw firstError;
1078
+ }
1079
+ if (bytesReadFromSource !== totalBytes) {
1080
+ throw new Error(
1081
+ `Data source produced ${bytesReadFromSource} bytes, but expected ${totalBytes} bytes. The stream may have ended prematurely, resulting in an incomplete upload.`
1082
+ );
1083
+ }
1084
+ const spAcks = Array.from(
1085
+ aggregatedAcks.entries()
1086
+ ).map(([slot, signature]) => ({ slot, signature }));
1087
+ return { spAcks };
1088
+ }
1089
+ /**
1090
+ * Downloads a blob from the Shelby RPC node.
1091
+ * Returns a streaming response with validation to ensure data integrity.
1092
+ *
1093
+ * @param params.account - The account that owns the blob.
1094
+ * @param params.blobName - The name/path of the blob (e.g. "folder/file.txt").
1095
+ * @param params.range - Optional byte range for partial downloads.
1096
+ * @param params.range.start - Starting byte position (inclusive).
1097
+ * @param params.range.end - Ending byte position (inclusive, optional).
1098
+ * @param params.micropayment - Optional micropayment to attach to the request.
1099
+ *
1100
+ * @returns A ShelbyBlob object containing the account, name, readable stream, and content length.
1101
+ *
1102
+ * @throws Error if the download fails or content length doesn't match.
1103
+ * @throws StaleChannelStateError if the micropayment is stale (server has newer state).
1104
+ *
1105
+ * @example
1106
+ * ```typescript
1107
+ * // Download entire blob
1108
+ * const blob = await client.getBlob({
1109
+ * account: AccountAddress.from("0x1"),
1110
+ * blobName: "documents/report.pdf"
1111
+ * });
1112
+ *
1113
+ * // Download partial content (bytes 100-199)
1114
+ * const partial = await client.getBlob({
1115
+ * account: AccountAddress.from("0x1"),
1116
+ * blobName: "large-file.bin",
1117
+ * range: { start: 100, end: 199 }
1118
+ * });
1119
+ *
1120
+ * // Download with micropayment
1121
+ * const blob = await client.getBlob({
1122
+ * account: AccountAddress.from("0x1"),
1123
+ * blobName: "documents/report.pdf",
1124
+ * micropayment: senderBuiltMicropayment
1125
+ * });
1126
+ * ```
1127
+ */
1128
+ async getBlob(params) {
1129
+ BlobNameSchema.parse(params.blobName);
1130
+ const url = buildRequestUrl(
1131
+ `/v1/blobs/${params.account.toString()}/${encodeURIComponentKeepSlashes(
1132
+ params.blobName
1133
+ )}`,
1134
+ this.baseUrl
1135
+ );
1136
+ const headers = new Headers();
1137
+ if (params.range !== void 0) {
1138
+ const { start, end } = params.range;
1139
+ if (end === void 0) {
1140
+ headers.set("Range", `bytes=${start}-`);
1141
+ } else {
1142
+ if (end < start) {
1143
+ throw new Error("Range end cannot be less than start.");
1144
+ }
1145
+ headers.set("Range", `bytes=${start}-${end}`);
1146
+ }
1147
+ }
1148
+ if (this.apiKey) {
1149
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
1150
+ }
1151
+ if (params.micropayment) {
1152
+ const bytes = params.micropayment.bcsToBytes();
1153
+ const binaryString = Array.from(
1154
+ bytes,
1155
+ (byte) => String.fromCharCode(byte)
1156
+ ).join("");
1157
+ headers.set(MICROPAYMENT_HEADER, btoa(binaryString));
1158
+ }
1159
+ const response = await fetch(url, { headers });
1160
+ if (response.status === 409) {
1161
+ let json;
1162
+ try {
1163
+ json = await response.json();
1164
+ } catch {
1165
+ throw new Error(
1166
+ `Failed to download blob: ${response.status} ${response.statusText}`
1167
+ );
1168
+ }
1169
+ const parseResult = StaleMicropaymentErrorResponseSchema.safeParse(json);
1170
+ if (!parseResult.success) {
1171
+ throw new Error(
1172
+ `Failed to download blob: ${response.status} ${response.statusText}`
1173
+ );
1174
+ }
1175
+ const errorBody = parseResult.data;
1176
+ if (errorBody.storedMicropayment) {
1177
+ throw StaleChannelStateError.fromBase64(
1178
+ errorBody.storedMicropayment,
1179
+ errorBody.error
1180
+ );
1181
+ }
1182
+ throw new Error(
1183
+ errorBody.error ?? `Failed to download blob: ${response.status} ${response.statusText}`
1184
+ );
1185
+ }
1186
+ if (!response.ok) {
1187
+ throw new Error(
1188
+ `Failed to download blob: ${response.status} ${response.statusText}`
1189
+ );
1190
+ }
1191
+ if (!response.body) {
1192
+ throw new Error("Response body is null");
1193
+ }
1194
+ const contentLengthHeader = response.headers.get("content-length");
1195
+ if (contentLengthHeader === null) {
1196
+ throw new Error(
1197
+ "Response did not have content-length header, which is required"
1198
+ );
1199
+ }
1200
+ const expectedContentLength = Number.parseInt(contentLengthHeader, 10);
1201
+ if (Number.isNaN(expectedContentLength)) {
1202
+ throw new Error(
1203
+ `Invalid content-length header received: ${contentLengthHeader}`
1204
+ );
1205
+ }
1206
+ const validatingStream = new ReadableStream({
1207
+ start(controller) {
1208
+ const maybeReader = response.body?.getReader();
1209
+ if (!maybeReader) {
1210
+ controller.error(new Error("Response body reader is unavailable"));
1211
+ return;
1212
+ }
1213
+ const reader = maybeReader;
1214
+ let bytesReceived = 0;
1215
+ function pump() {
1216
+ return reader.read().then(({ done, value }) => {
1217
+ if (done) {
1218
+ if (bytesReceived !== expectedContentLength) {
1219
+ controller.error(
1220
+ new Error(
1221
+ `Downloaded data size (${bytesReceived} bytes) does not match content-length header (${expectedContentLength} bytes). This might indicate a partial or corrupted download.`
1222
+ )
1223
+ );
1224
+ return;
1225
+ }
1226
+ controller.close();
1227
+ return;
1228
+ }
1229
+ bytesReceived += value.byteLength;
1230
+ controller.enqueue(value);
1231
+ return pump();
1232
+ }).catch((error) => {
1233
+ controller.error(error);
1234
+ });
1235
+ }
1236
+ return pump();
1237
+ }
1238
+ });
1239
+ return {
1240
+ account: normalizeAddress(params.account),
1241
+ name: params.blobName,
1242
+ readable: validatingStream,
1243
+ contentLength: expectedContentLength
1244
+ };
1245
+ }
1246
+ };
14
1247
  export {
15
1248
  ShelbyRPCClient
16
1249
  };