@shelby-protocol/sdk 0.2.4 → 0.3.1

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