@3mate/walrus-sponsor-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 3mate Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @3mate/walrus-sponsor-sdk
2
+
3
+ Official SDK for [3mate Walrus Sponsor](https://github.com/3MateLabs/walrus-sponsor-sdk) — sponsored blob storage on the [Walrus](https://walrus.space) network.
4
+
5
+ Zero runtime dependencies. Works in Node.js 18+ and modern browsers.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @3mate/walrus-sponsor-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { WalrusSponsor } from "@3mate/walrus-sponsor-sdk";
17
+
18
+ const walrus = new WalrusSponsor({
19
+ apiKey: "sbk_live_...",
20
+ baseUrl: "https://your-backend.com",
21
+ });
22
+
23
+ // Upload a file (browser)
24
+ const file = document.querySelector<HTMLInputElement>("#file")!.files![0];
25
+ const result = await walrus.upload(file, {
26
+ creatorAddress: "0x123...",
27
+ epochs: 3,
28
+ });
29
+
30
+ console.log("View:", walrus.getBlobUrl(result.blobId));
31
+ ```
32
+
33
+ ## API
34
+
35
+ ### Constructor
36
+
37
+ ```typescript
38
+ new WalrusSponsor({
39
+ apiKey: string; // Required — your API key (sbk_live_...)
40
+ baseUrl: string; // Required — your backend URL
41
+ aggregatorUrl?: string; // Optional — defaults to mainnet aggregator
42
+ });
43
+ ```
44
+
45
+ ### Methods
46
+
47
+ | Method | Description |
48
+ |--------|-------------|
49
+ | `health()` | Check if backend is live |
50
+ | `epoch()` | Get current Walrus epoch info |
51
+ | `estimateCost(size, epochs?)` | Estimate WAL storage cost |
52
+ | `upload(file, opts)` | Upload a file via sponsored publisher |
53
+ | `listBlobs(opts?)` | List blobs with optional filters |
54
+ | `getBlob(id)` | Get single blob details |
55
+ | `getBlobUrl(blobId)` | Build aggregator URL (no network call) |
56
+
57
+ ### Upload
58
+
59
+ ```typescript
60
+ // Browser — File or Blob
61
+ await walrus.upload(file, { creatorAddress: "0x..." });
62
+
63
+ // Node.js — Buffer
64
+ await walrus.upload(
65
+ { data: buffer, filename: "photo.png", contentType: "image/png" },
66
+ { creatorAddress: "0x...", epochs: 3 }
67
+ );
68
+ ```
69
+
70
+ Options:
71
+
72
+ | Option | Type | Default | Description |
73
+ |--------|------|---------|-------------|
74
+ | `creatorAddress` | `string` | — | Required. Sui address to own the blob |
75
+ | `epochs` | `number` | `1` | Storage duration in epochs |
76
+ | `deletable` | `boolean` | `true` | Whether the blob can be deleted |
77
+
78
+ ### List & Get Blobs
79
+
80
+ ```typescript
81
+ const { blobs, total } = await walrus.listBlobs({
82
+ status: "active",
83
+ creatorAddress: "0x...",
84
+ limit: 10,
85
+ offset: 0,
86
+ });
87
+
88
+ const blob = await walrus.getBlob("0x1234...");
89
+ ```
90
+
91
+ ### Estimate Cost
92
+
93
+ ```typescript
94
+ const cost = await walrus.estimateCost(1024 * 1024, 3); // 1 MB, 3 epochs
95
+ console.log(cost.totalCost); // WAL amount as string
96
+ console.log(cost.storageCost); // storage component
97
+ console.log(cost.writeCost); // write component
98
+ ```
99
+
100
+ ### Error Handling
101
+
102
+ ```typescript
103
+ import { WalrusSponsorError } from "@3mate/walrus-sponsor-sdk";
104
+
105
+ try {
106
+ await walrus.upload(file, opts);
107
+ } catch (err) {
108
+ if (err instanceof WalrusSponsorError) {
109
+ console.error(err.message); // Human-readable message
110
+ console.error(err.statusCode); // 401, 402, 502, etc.
111
+ console.error(err.details); // Raw API error body
112
+ }
113
+ }
114
+ ```
115
+
116
+ | Status | Meaning |
117
+ |--------|---------|
118
+ | 401 | Invalid or missing API key |
119
+ | 402 | Insufficient workspace balance |
120
+ | 502 | Storage service unavailable |
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,199 @@
1
+ interface WalrusSponsorConfig {
2
+ /** API key (starts with sbk_live_) */
3
+ apiKey: string;
4
+ /** Base URL of your walrus-sponsor backend */
5
+ baseUrl: string;
6
+ /** Walrus aggregator URL for blob retrieval (default: mainnet aggregator) */
7
+ aggregatorUrl?: string;
8
+ }
9
+ interface UploadOptions {
10
+ /** Sui address that will own the SponsoredBlob (0x-prefixed, 64 hex chars) */
11
+ creatorAddress: string;
12
+ /** Storage duration in epochs (default: 1) */
13
+ epochs?: number;
14
+ /** Whether the blob can be deleted (default: true) */
15
+ deletable?: boolean;
16
+ }
17
+ interface ListBlobsOptions {
18
+ /** Filter by status */
19
+ status?: "active" | "deleted" | "expired" | "transferred";
20
+ /** Filter by creator address */
21
+ creatorAddress?: string;
22
+ /** Max results (default: 50) */
23
+ limit?: number;
24
+ /** Pagination offset (default: 0) */
25
+ offset?: number;
26
+ }
27
+ interface HealthResponse {
28
+ status: string;
29
+ timestamp: string;
30
+ }
31
+ interface EpochResponse {
32
+ currentEpoch: number;
33
+ startTime: string;
34
+ endTime: string;
35
+ epochDurationDays: number;
36
+ }
37
+ interface StorageCostEstimate {
38
+ size: number;
39
+ epochs: number;
40
+ storageCost: string;
41
+ writeCost: string;
42
+ totalCost: string;
43
+ totalCostNumber: number;
44
+ }
45
+ interface SponsoredBlob {
46
+ sponsoredBlobId: string;
47
+ blobId: string;
48
+ creatorAddress: string;
49
+ sponsorAddress: string;
50
+ txDigest: string;
51
+ sizeBytes: number;
52
+ walCost: number;
53
+ gasFee: number;
54
+ totalCharged: number;
55
+ endEpoch: number;
56
+ status: string;
57
+ }
58
+ interface AlreadyCertifiedBlob {
59
+ blobId: string;
60
+ endEpoch: number;
61
+ alreadyCertified: true;
62
+ txDigest: string;
63
+ }
64
+ type UploadResult = SponsoredBlob | AlreadyCertifiedBlob;
65
+ interface BlobSummary {
66
+ id: string;
67
+ objectId: string;
68
+ blobId: string;
69
+ creatorAddress: string;
70
+ sponsorAddress: string;
71
+ sizeBytes: number;
72
+ contentType: string | null;
73
+ filename: string | null;
74
+ endEpoch: number;
75
+ status: string;
76
+ createdAt: string;
77
+ }
78
+ interface BlobDetail extends BlobSummary {
79
+ txDigest: string;
80
+ sponsorCanDelete: boolean;
81
+ autoRenew: boolean;
82
+ autoRenewEpochs: number | null;
83
+ updatedAt: string;
84
+ }
85
+ interface ListBlobsResponse {
86
+ blobs: BlobSummary[];
87
+ total: number;
88
+ }
89
+ /** Browser File/Blob or Node.js Buffer with metadata */
90
+ type FileInput = File | Blob | Buffer | {
91
+ data: Buffer;
92
+ filename: string;
93
+ contentType?: string;
94
+ };
95
+ declare class WalrusSponsorError extends Error {
96
+ statusCode: number;
97
+ details?: any;
98
+ constructor(message: string, statusCode: number, details?: any);
99
+ }
100
+ declare class WalrusSponsor {
101
+ private apiKey;
102
+ private baseUrl;
103
+ private aggregatorUrl;
104
+ constructor(config: WalrusSponsorConfig);
105
+ private request;
106
+ /**
107
+ * Check backend health (no auth required).
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const h = await walrus.health();
112
+ * console.log(h.status); // "ok"
113
+ * ```
114
+ */
115
+ health(): Promise<HealthResponse>;
116
+ /**
117
+ * Get current Walrus epoch info (no auth required).
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * const e = await walrus.epoch();
122
+ * console.log(e.currentEpoch);
123
+ * ```
124
+ */
125
+ epoch(): Promise<EpochResponse>;
126
+ /**
127
+ * Estimate WAL storage cost for a given file size and epoch count.
128
+ *
129
+ * @param size - File size in bytes
130
+ * @param epochs - Storage duration in epochs (default: 1)
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * const cost = await walrus.estimateCost(1024 * 1024, 3);
135
+ * console.log(cost.totalCost);
136
+ * ```
137
+ */
138
+ estimateCost(size: number, epochs?: number): Promise<StorageCostEstimate>;
139
+ /**
140
+ * Upload a file to Walrus via the sponsored publisher.
141
+ *
142
+ * @param file - File, Blob, Buffer, or { data, filename, contentType }
143
+ * @param opts - Upload options (creatorAddress required)
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // Browser
148
+ * const input = document.querySelector<HTMLInputElement>("#file");
149
+ * const result = await walrus.upload(input.files[0], {
150
+ * creatorAddress: "0x123...",
151
+ * epochs: 3,
152
+ * });
153
+ *
154
+ * // Node.js
155
+ * const buf = fs.readFileSync("photo.png");
156
+ * const result = await walrus.upload(
157
+ * { data: buf, filename: "photo.png", contentType: "image/png" },
158
+ * { creatorAddress: "0x123...", epochs: 3 }
159
+ * );
160
+ * ```
161
+ */
162
+ upload(file: FileInput, opts: UploadOptions): Promise<UploadResult>;
163
+ /**
164
+ * List sponsored blobs for the current API key.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const { blobs, total } = await walrus.listBlobs({ status: "active", limit: 10 });
169
+ * ```
170
+ */
171
+ listBlobs(opts?: ListBlobsOptions): Promise<ListBlobsResponse>;
172
+ /**
173
+ * Get details of a single sponsored blob.
174
+ *
175
+ * @param id - Blob object_id or database id
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * const blob = await walrus.getBlob("0x1234...");
180
+ * console.log(blob.status, blob.endEpoch);
181
+ * ```
182
+ */
183
+ getBlob(id: string): Promise<BlobDetail>;
184
+ /**
185
+ * Build the aggregator URL for retrieving a blob's content.
186
+ * This is a client-side helper — no network call.
187
+ *
188
+ * @param blobId - The Walrus blob ID (base64url encoded)
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * const url = walrus.getBlobUrl("Aqz5l...");
193
+ * // => "https://aggregator.walrus-mainnet.walrus.space/v1/Aqz5l..."
194
+ * ```
195
+ */
196
+ getBlobUrl(blobId: string): string;
197
+ }
198
+
199
+ export { type AlreadyCertifiedBlob, type BlobDetail, type BlobSummary, type EpochResponse, type FileInput, type HealthResponse, type ListBlobsOptions, type ListBlobsResponse, type SponsoredBlob, type StorageCostEstimate, type UploadOptions, type UploadResult, WalrusSponsor, type WalrusSponsorConfig, WalrusSponsorError, WalrusSponsor as default };
@@ -0,0 +1,199 @@
1
+ interface WalrusSponsorConfig {
2
+ /** API key (starts with sbk_live_) */
3
+ apiKey: string;
4
+ /** Base URL of your walrus-sponsor backend */
5
+ baseUrl: string;
6
+ /** Walrus aggregator URL for blob retrieval (default: mainnet aggregator) */
7
+ aggregatorUrl?: string;
8
+ }
9
+ interface UploadOptions {
10
+ /** Sui address that will own the SponsoredBlob (0x-prefixed, 64 hex chars) */
11
+ creatorAddress: string;
12
+ /** Storage duration in epochs (default: 1) */
13
+ epochs?: number;
14
+ /** Whether the blob can be deleted (default: true) */
15
+ deletable?: boolean;
16
+ }
17
+ interface ListBlobsOptions {
18
+ /** Filter by status */
19
+ status?: "active" | "deleted" | "expired" | "transferred";
20
+ /** Filter by creator address */
21
+ creatorAddress?: string;
22
+ /** Max results (default: 50) */
23
+ limit?: number;
24
+ /** Pagination offset (default: 0) */
25
+ offset?: number;
26
+ }
27
+ interface HealthResponse {
28
+ status: string;
29
+ timestamp: string;
30
+ }
31
+ interface EpochResponse {
32
+ currentEpoch: number;
33
+ startTime: string;
34
+ endTime: string;
35
+ epochDurationDays: number;
36
+ }
37
+ interface StorageCostEstimate {
38
+ size: number;
39
+ epochs: number;
40
+ storageCost: string;
41
+ writeCost: string;
42
+ totalCost: string;
43
+ totalCostNumber: number;
44
+ }
45
+ interface SponsoredBlob {
46
+ sponsoredBlobId: string;
47
+ blobId: string;
48
+ creatorAddress: string;
49
+ sponsorAddress: string;
50
+ txDigest: string;
51
+ sizeBytes: number;
52
+ walCost: number;
53
+ gasFee: number;
54
+ totalCharged: number;
55
+ endEpoch: number;
56
+ status: string;
57
+ }
58
+ interface AlreadyCertifiedBlob {
59
+ blobId: string;
60
+ endEpoch: number;
61
+ alreadyCertified: true;
62
+ txDigest: string;
63
+ }
64
+ type UploadResult = SponsoredBlob | AlreadyCertifiedBlob;
65
+ interface BlobSummary {
66
+ id: string;
67
+ objectId: string;
68
+ blobId: string;
69
+ creatorAddress: string;
70
+ sponsorAddress: string;
71
+ sizeBytes: number;
72
+ contentType: string | null;
73
+ filename: string | null;
74
+ endEpoch: number;
75
+ status: string;
76
+ createdAt: string;
77
+ }
78
+ interface BlobDetail extends BlobSummary {
79
+ txDigest: string;
80
+ sponsorCanDelete: boolean;
81
+ autoRenew: boolean;
82
+ autoRenewEpochs: number | null;
83
+ updatedAt: string;
84
+ }
85
+ interface ListBlobsResponse {
86
+ blobs: BlobSummary[];
87
+ total: number;
88
+ }
89
+ /** Browser File/Blob or Node.js Buffer with metadata */
90
+ type FileInput = File | Blob | Buffer | {
91
+ data: Buffer;
92
+ filename: string;
93
+ contentType?: string;
94
+ };
95
+ declare class WalrusSponsorError extends Error {
96
+ statusCode: number;
97
+ details?: any;
98
+ constructor(message: string, statusCode: number, details?: any);
99
+ }
100
+ declare class WalrusSponsor {
101
+ private apiKey;
102
+ private baseUrl;
103
+ private aggregatorUrl;
104
+ constructor(config: WalrusSponsorConfig);
105
+ private request;
106
+ /**
107
+ * Check backend health (no auth required).
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const h = await walrus.health();
112
+ * console.log(h.status); // "ok"
113
+ * ```
114
+ */
115
+ health(): Promise<HealthResponse>;
116
+ /**
117
+ * Get current Walrus epoch info (no auth required).
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * const e = await walrus.epoch();
122
+ * console.log(e.currentEpoch);
123
+ * ```
124
+ */
125
+ epoch(): Promise<EpochResponse>;
126
+ /**
127
+ * Estimate WAL storage cost for a given file size and epoch count.
128
+ *
129
+ * @param size - File size in bytes
130
+ * @param epochs - Storage duration in epochs (default: 1)
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * const cost = await walrus.estimateCost(1024 * 1024, 3);
135
+ * console.log(cost.totalCost);
136
+ * ```
137
+ */
138
+ estimateCost(size: number, epochs?: number): Promise<StorageCostEstimate>;
139
+ /**
140
+ * Upload a file to Walrus via the sponsored publisher.
141
+ *
142
+ * @param file - File, Blob, Buffer, or { data, filename, contentType }
143
+ * @param opts - Upload options (creatorAddress required)
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // Browser
148
+ * const input = document.querySelector<HTMLInputElement>("#file");
149
+ * const result = await walrus.upload(input.files[0], {
150
+ * creatorAddress: "0x123...",
151
+ * epochs: 3,
152
+ * });
153
+ *
154
+ * // Node.js
155
+ * const buf = fs.readFileSync("photo.png");
156
+ * const result = await walrus.upload(
157
+ * { data: buf, filename: "photo.png", contentType: "image/png" },
158
+ * { creatorAddress: "0x123...", epochs: 3 }
159
+ * );
160
+ * ```
161
+ */
162
+ upload(file: FileInput, opts: UploadOptions): Promise<UploadResult>;
163
+ /**
164
+ * List sponsored blobs for the current API key.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const { blobs, total } = await walrus.listBlobs({ status: "active", limit: 10 });
169
+ * ```
170
+ */
171
+ listBlobs(opts?: ListBlobsOptions): Promise<ListBlobsResponse>;
172
+ /**
173
+ * Get details of a single sponsored blob.
174
+ *
175
+ * @param id - Blob object_id or database id
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * const blob = await walrus.getBlob("0x1234...");
180
+ * console.log(blob.status, blob.endEpoch);
181
+ * ```
182
+ */
183
+ getBlob(id: string): Promise<BlobDetail>;
184
+ /**
185
+ * Build the aggregator URL for retrieving a blob's content.
186
+ * This is a client-side helper — no network call.
187
+ *
188
+ * @param blobId - The Walrus blob ID (base64url encoded)
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * const url = walrus.getBlobUrl("Aqz5l...");
193
+ * // => "https://aggregator.walrus-mainnet.walrus.space/v1/Aqz5l..."
194
+ * ```
195
+ */
196
+ getBlobUrl(blobId: string): string;
197
+ }
198
+
199
+ export { type AlreadyCertifiedBlob, type BlobDetail, type BlobSummary, type EpochResponse, type FileInput, type HealthResponse, type ListBlobsOptions, type ListBlobsResponse, type SponsoredBlob, type StorageCostEstimate, type UploadOptions, type UploadResult, WalrusSponsor, type WalrusSponsorConfig, WalrusSponsorError, WalrusSponsor as default };
package/dist/index.js ADDED
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ WalrusSponsor: () => WalrusSponsor,
24
+ WalrusSponsorError: () => WalrusSponsorError,
25
+ default: () => index_default
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var DEFAULT_AGGREGATOR_URL = "https://aggregator.walrus-mainnet.walrus.space";
29
+ var ERROR_MAP = {
30
+ 401: "Invalid or missing API key",
31
+ 402: "Insufficient workspace balance",
32
+ 404: "Resource not found",
33
+ 502: "Storage service unavailable \u2014 publisher may be down"
34
+ };
35
+ var WalrusSponsorError = class extends Error {
36
+ constructor(message, statusCode, details) {
37
+ super(message);
38
+ this.name = "WalrusSponsorError";
39
+ this.statusCode = statusCode;
40
+ this.details = details;
41
+ }
42
+ };
43
+ function toCamelCase(obj) {
44
+ const out = {};
45
+ for (const key of Object.keys(obj)) {
46
+ const camel = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
47
+ const val = obj[key];
48
+ out[camel] = val !== null && typeof val === "object" && !Array.isArray(val) ? toCamelCase(val) : val;
49
+ }
50
+ return out;
51
+ }
52
+ function buildFormData(file, opts) {
53
+ const fd = new FormData();
54
+ if (typeof File !== "undefined" && file instanceof File) {
55
+ fd.append("file", file);
56
+ } else if (typeof Blob !== "undefined" && file instanceof Blob) {
57
+ fd.append("file", file, "upload");
58
+ } else if (Buffer.isBuffer(file)) {
59
+ fd.append("file", new Blob([new Uint8Array(file)]), "upload");
60
+ } else if (typeof file === "object" && "data" in file && Buffer.isBuffer(file.data)) {
61
+ const blob = new Blob([new Uint8Array(file.data)], {
62
+ type: file.contentType || "application/octet-stream"
63
+ });
64
+ fd.append("file", blob, file.filename);
65
+ } else {
66
+ throw new WalrusSponsorError(
67
+ "Invalid file input \u2014 provide a File, Blob, Buffer, or { data, filename }",
68
+ 400
69
+ );
70
+ }
71
+ fd.append("creator_address", opts.creatorAddress);
72
+ if (opts.epochs !== void 0) fd.append("epochs", String(opts.epochs));
73
+ if (opts.deletable !== void 0)
74
+ fd.append("deletable", String(opts.deletable));
75
+ return fd;
76
+ }
77
+ var WalrusSponsor = class {
78
+ constructor(config) {
79
+ if (!config.apiKey) throw new Error("apiKey is required");
80
+ if (!config.baseUrl) throw new Error("baseUrl is required");
81
+ this.apiKey = config.apiKey;
82
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
83
+ this.aggregatorUrl = (config.aggregatorUrl || DEFAULT_AGGREGATOR_URL).replace(/\/+$/, "");
84
+ }
85
+ // ── Internal fetch wrapper ────────────────────────────────────────────
86
+ async request(method, path, opts) {
87
+ const auth = opts?.auth ?? true;
88
+ let url = `${this.baseUrl}${path}`;
89
+ if (opts?.query) {
90
+ const params = new URLSearchParams();
91
+ for (const [k, v] of Object.entries(opts.query)) {
92
+ if (v !== void 0) params.append(k, String(v));
93
+ }
94
+ const qs = params.toString();
95
+ if (qs) url += `?${qs}`;
96
+ }
97
+ const headers = {};
98
+ if (auth) headers["Authorization"] = `Bearer ${this.apiKey}`;
99
+ let body;
100
+ if (opts?.formData) {
101
+ body = opts.formData;
102
+ } else if (opts?.body) {
103
+ headers["Content-Type"] = "application/json";
104
+ body = JSON.stringify(opts.body);
105
+ }
106
+ let res;
107
+ try {
108
+ res = await fetch(url, { method, headers, body });
109
+ } catch (err) {
110
+ throw new WalrusSponsorError(
111
+ `Network error: ${err instanceof Error ? err.message : "Unknown error"}`,
112
+ 0,
113
+ err
114
+ );
115
+ }
116
+ if (!res.ok) {
117
+ let message = ERROR_MAP[res.status] || `Request failed (${res.status})`;
118
+ let details;
119
+ try {
120
+ const data2 = await res.json();
121
+ if (data2.error) message = data2.error;
122
+ details = data2;
123
+ } catch {
124
+ }
125
+ throw new WalrusSponsorError(message, res.status, details);
126
+ }
127
+ const data = await res.json();
128
+ return data;
129
+ }
130
+ // ── Public methods ────────────────────────────────────────────────────
131
+ /**
132
+ * Check backend health (no auth required).
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * const h = await walrus.health();
137
+ * console.log(h.status); // "ok"
138
+ * ```
139
+ */
140
+ async health() {
141
+ return this.request("GET", "/health", { auth: false });
142
+ }
143
+ /**
144
+ * Get current Walrus epoch info (no auth required).
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * const e = await walrus.epoch();
149
+ * console.log(e.currentEpoch);
150
+ * ```
151
+ */
152
+ async epoch() {
153
+ const raw = await this.request("GET", "/epoch", { auth: false });
154
+ return toCamelCase(raw);
155
+ }
156
+ /**
157
+ * Estimate WAL storage cost for a given file size and epoch count.
158
+ *
159
+ * @param size - File size in bytes
160
+ * @param epochs - Storage duration in epochs (default: 1)
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const cost = await walrus.estimateCost(1024 * 1024, 3);
165
+ * console.log(cost.totalCost);
166
+ * ```
167
+ */
168
+ async estimateCost(size, epochs) {
169
+ const raw = await this.request("GET", "/v1/storage-cost", {
170
+ query: { size, epochs }
171
+ });
172
+ return toCamelCase(raw);
173
+ }
174
+ /**
175
+ * Upload a file to Walrus via the sponsored publisher.
176
+ *
177
+ * @param file - File, Blob, Buffer, or { data, filename, contentType }
178
+ * @param opts - Upload options (creatorAddress required)
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * // Browser
183
+ * const input = document.querySelector<HTMLInputElement>("#file");
184
+ * const result = await walrus.upload(input.files[0], {
185
+ * creatorAddress: "0x123...",
186
+ * epochs: 3,
187
+ * });
188
+ *
189
+ * // Node.js
190
+ * const buf = fs.readFileSync("photo.png");
191
+ * const result = await walrus.upload(
192
+ * { data: buf, filename: "photo.png", contentType: "image/png" },
193
+ * { creatorAddress: "0x123...", epochs: 3 }
194
+ * );
195
+ * ```
196
+ */
197
+ async upload(file, opts) {
198
+ if (!opts.creatorAddress)
199
+ throw new WalrusSponsorError("creatorAddress is required", 400);
200
+ const fd = buildFormData(file, opts);
201
+ const raw = await this.request("POST", "/v1/upload", {
202
+ formData: fd
203
+ });
204
+ return toCamelCase(raw);
205
+ }
206
+ /**
207
+ * List sponsored blobs for the current API key.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * const { blobs, total } = await walrus.listBlobs({ status: "active", limit: 10 });
212
+ * ```
213
+ */
214
+ async listBlobs(opts) {
215
+ const raw = await this.request("GET", "/v1/blobs", {
216
+ query: {
217
+ status: opts?.status,
218
+ creator_address: opts?.creatorAddress,
219
+ limit: opts?.limit,
220
+ offset: opts?.offset
221
+ }
222
+ });
223
+ return {
224
+ blobs: (raw.blobs || []).map((b) => toCamelCase(b)),
225
+ total: raw.total
226
+ };
227
+ }
228
+ /**
229
+ * Get details of a single sponsored blob.
230
+ *
231
+ * @param id - Blob object_id or database id
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * const blob = await walrus.getBlob("0x1234...");
236
+ * console.log(blob.status, blob.endEpoch);
237
+ * ```
238
+ */
239
+ async getBlob(id) {
240
+ const raw = await this.request("GET", `/v1/blobs/${id}`);
241
+ return toCamelCase(raw);
242
+ }
243
+ /**
244
+ * Build the aggregator URL for retrieving a blob's content.
245
+ * This is a client-side helper — no network call.
246
+ *
247
+ * @param blobId - The Walrus blob ID (base64url encoded)
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * const url = walrus.getBlobUrl("Aqz5l...");
252
+ * // => "https://aggregator.walrus-mainnet.walrus.space/v1/Aqz5l..."
253
+ * ```
254
+ */
255
+ getBlobUrl(blobId) {
256
+ return `${this.aggregatorUrl}/v1/${blobId}`;
257
+ }
258
+ };
259
+ var index_default = WalrusSponsor;
260
+ // Annotate the CommonJS export names for ESM import in node:
261
+ 0 && (module.exports = {
262
+ WalrusSponsor,
263
+ WalrusSponsorError
264
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,238 @@
1
+ // src/index.ts
2
+ var DEFAULT_AGGREGATOR_URL = "https://aggregator.walrus-mainnet.walrus.space";
3
+ var ERROR_MAP = {
4
+ 401: "Invalid or missing API key",
5
+ 402: "Insufficient workspace balance",
6
+ 404: "Resource not found",
7
+ 502: "Storage service unavailable \u2014 publisher may be down"
8
+ };
9
+ var WalrusSponsorError = class extends Error {
10
+ constructor(message, statusCode, details) {
11
+ super(message);
12
+ this.name = "WalrusSponsorError";
13
+ this.statusCode = statusCode;
14
+ this.details = details;
15
+ }
16
+ };
17
+ function toCamelCase(obj) {
18
+ const out = {};
19
+ for (const key of Object.keys(obj)) {
20
+ const camel = key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
21
+ const val = obj[key];
22
+ out[camel] = val !== null && typeof val === "object" && !Array.isArray(val) ? toCamelCase(val) : val;
23
+ }
24
+ return out;
25
+ }
26
+ function buildFormData(file, opts) {
27
+ const fd = new FormData();
28
+ if (typeof File !== "undefined" && file instanceof File) {
29
+ fd.append("file", file);
30
+ } else if (typeof Blob !== "undefined" && file instanceof Blob) {
31
+ fd.append("file", file, "upload");
32
+ } else if (Buffer.isBuffer(file)) {
33
+ fd.append("file", new Blob([new Uint8Array(file)]), "upload");
34
+ } else if (typeof file === "object" && "data" in file && Buffer.isBuffer(file.data)) {
35
+ const blob = new Blob([new Uint8Array(file.data)], {
36
+ type: file.contentType || "application/octet-stream"
37
+ });
38
+ fd.append("file", blob, file.filename);
39
+ } else {
40
+ throw new WalrusSponsorError(
41
+ "Invalid file input \u2014 provide a File, Blob, Buffer, or { data, filename }",
42
+ 400
43
+ );
44
+ }
45
+ fd.append("creator_address", opts.creatorAddress);
46
+ if (opts.epochs !== void 0) fd.append("epochs", String(opts.epochs));
47
+ if (opts.deletable !== void 0)
48
+ fd.append("deletable", String(opts.deletable));
49
+ return fd;
50
+ }
51
+ var WalrusSponsor = class {
52
+ constructor(config) {
53
+ if (!config.apiKey) throw new Error("apiKey is required");
54
+ if (!config.baseUrl) throw new Error("baseUrl is required");
55
+ this.apiKey = config.apiKey;
56
+ this.baseUrl = config.baseUrl.replace(/\/+$/, "");
57
+ this.aggregatorUrl = (config.aggregatorUrl || DEFAULT_AGGREGATOR_URL).replace(/\/+$/, "");
58
+ }
59
+ // ── Internal fetch wrapper ────────────────────────────────────────────
60
+ async request(method, path, opts) {
61
+ const auth = opts?.auth ?? true;
62
+ let url = `${this.baseUrl}${path}`;
63
+ if (opts?.query) {
64
+ const params = new URLSearchParams();
65
+ for (const [k, v] of Object.entries(opts.query)) {
66
+ if (v !== void 0) params.append(k, String(v));
67
+ }
68
+ const qs = params.toString();
69
+ if (qs) url += `?${qs}`;
70
+ }
71
+ const headers = {};
72
+ if (auth) headers["Authorization"] = `Bearer ${this.apiKey}`;
73
+ let body;
74
+ if (opts?.formData) {
75
+ body = opts.formData;
76
+ } else if (opts?.body) {
77
+ headers["Content-Type"] = "application/json";
78
+ body = JSON.stringify(opts.body);
79
+ }
80
+ let res;
81
+ try {
82
+ res = await fetch(url, { method, headers, body });
83
+ } catch (err) {
84
+ throw new WalrusSponsorError(
85
+ `Network error: ${err instanceof Error ? err.message : "Unknown error"}`,
86
+ 0,
87
+ err
88
+ );
89
+ }
90
+ if (!res.ok) {
91
+ let message = ERROR_MAP[res.status] || `Request failed (${res.status})`;
92
+ let details;
93
+ try {
94
+ const data2 = await res.json();
95
+ if (data2.error) message = data2.error;
96
+ details = data2;
97
+ } catch {
98
+ }
99
+ throw new WalrusSponsorError(message, res.status, details);
100
+ }
101
+ const data = await res.json();
102
+ return data;
103
+ }
104
+ // ── Public methods ────────────────────────────────────────────────────
105
+ /**
106
+ * Check backend health (no auth required).
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const h = await walrus.health();
111
+ * console.log(h.status); // "ok"
112
+ * ```
113
+ */
114
+ async health() {
115
+ return this.request("GET", "/health", { auth: false });
116
+ }
117
+ /**
118
+ * Get current Walrus epoch info (no auth required).
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const e = await walrus.epoch();
123
+ * console.log(e.currentEpoch);
124
+ * ```
125
+ */
126
+ async epoch() {
127
+ const raw = await this.request("GET", "/epoch", { auth: false });
128
+ return toCamelCase(raw);
129
+ }
130
+ /**
131
+ * Estimate WAL storage cost for a given file size and epoch count.
132
+ *
133
+ * @param size - File size in bytes
134
+ * @param epochs - Storage duration in epochs (default: 1)
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * const cost = await walrus.estimateCost(1024 * 1024, 3);
139
+ * console.log(cost.totalCost);
140
+ * ```
141
+ */
142
+ async estimateCost(size, epochs) {
143
+ const raw = await this.request("GET", "/v1/storage-cost", {
144
+ query: { size, epochs }
145
+ });
146
+ return toCamelCase(raw);
147
+ }
148
+ /**
149
+ * Upload a file to Walrus via the sponsored publisher.
150
+ *
151
+ * @param file - File, Blob, Buffer, or { data, filename, contentType }
152
+ * @param opts - Upload options (creatorAddress required)
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * // Browser
157
+ * const input = document.querySelector<HTMLInputElement>("#file");
158
+ * const result = await walrus.upload(input.files[0], {
159
+ * creatorAddress: "0x123...",
160
+ * epochs: 3,
161
+ * });
162
+ *
163
+ * // Node.js
164
+ * const buf = fs.readFileSync("photo.png");
165
+ * const result = await walrus.upload(
166
+ * { data: buf, filename: "photo.png", contentType: "image/png" },
167
+ * { creatorAddress: "0x123...", epochs: 3 }
168
+ * );
169
+ * ```
170
+ */
171
+ async upload(file, opts) {
172
+ if (!opts.creatorAddress)
173
+ throw new WalrusSponsorError("creatorAddress is required", 400);
174
+ const fd = buildFormData(file, opts);
175
+ const raw = await this.request("POST", "/v1/upload", {
176
+ formData: fd
177
+ });
178
+ return toCamelCase(raw);
179
+ }
180
+ /**
181
+ * List sponsored blobs for the current API key.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * const { blobs, total } = await walrus.listBlobs({ status: "active", limit: 10 });
186
+ * ```
187
+ */
188
+ async listBlobs(opts) {
189
+ const raw = await this.request("GET", "/v1/blobs", {
190
+ query: {
191
+ status: opts?.status,
192
+ creator_address: opts?.creatorAddress,
193
+ limit: opts?.limit,
194
+ offset: opts?.offset
195
+ }
196
+ });
197
+ return {
198
+ blobs: (raw.blobs || []).map((b) => toCamelCase(b)),
199
+ total: raw.total
200
+ };
201
+ }
202
+ /**
203
+ * Get details of a single sponsored blob.
204
+ *
205
+ * @param id - Blob object_id or database id
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const blob = await walrus.getBlob("0x1234...");
210
+ * console.log(blob.status, blob.endEpoch);
211
+ * ```
212
+ */
213
+ async getBlob(id) {
214
+ const raw = await this.request("GET", `/v1/blobs/${id}`);
215
+ return toCamelCase(raw);
216
+ }
217
+ /**
218
+ * Build the aggregator URL for retrieving a blob's content.
219
+ * This is a client-side helper — no network call.
220
+ *
221
+ * @param blobId - The Walrus blob ID (base64url encoded)
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * const url = walrus.getBlobUrl("Aqz5l...");
226
+ * // => "https://aggregator.walrus-mainnet.walrus.space/v1/Aqz5l..."
227
+ * ```
228
+ */
229
+ getBlobUrl(blobId) {
230
+ return `${this.aggregatorUrl}/v1/${blobId}`;
231
+ }
232
+ };
233
+ var index_default = WalrusSponsor;
234
+ export {
235
+ WalrusSponsor,
236
+ WalrusSponsorError,
237
+ index_default as default
238
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@3mate/walrus-sponsor-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official SDK for 3mate Walrus Sponsor — Sponsored blob storage on Walrus",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
13
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "walrus",
18
+ "sui",
19
+ "blob",
20
+ "storage",
21
+ "sponsor",
22
+ "web3",
23
+ "3mate"
24
+ ],
25
+ "author": "3mate Labs",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/3MateLabs/walrus-sponsor-sdk"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^25.3.0",
33
+ "tsup": "^8.0.1",
34
+ "typescript": "^5.3.3"
35
+ }
36
+ }