tensorflow-sui2 1.0.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.
@@ -0,0 +1,58 @@
1
+ import { SuiClient } from "@mysten/sui/client";
2
+ import { Transaction } from "@mysten/sui/transactions";
3
+ import { OpenGraphClientConfig, DatasetMetadata, WalrusStorageInfo } from "./types.js";
4
+ export declare class OpenGraphClient {
5
+ private networkUrl;
6
+ private packageId;
7
+ private gasBudget;
8
+ suiClient: SuiClient;
9
+ private walrusNetwork;
10
+ private walrusPublisherUrl;
11
+ private walrusAggregatorUrl;
12
+ constructor(config: OpenGraphClientConfig);
13
+ getNetworkUrl(): string;
14
+ getPackageId(): string;
15
+ getGasBudget(): number;
16
+ getWalrusNetwork(): string;
17
+ getWalrusPublisherUrl(): string;
18
+ getWalrusAggregatorUrl(): string;
19
+ getConfig(): OpenGraphClientConfig;
20
+ setNetworkUrl(url: string): void;
21
+ setPackageId(packageId: string): void;
22
+ setGasBudget(gasBudget: number): void;
23
+ setWalrusNetwork(network: string): void;
24
+ setWalrusPublisherUrl(url: string): void;
25
+ setWalrusAggregatorUrl(url: string): void;
26
+ private getSuiScanUrl;
27
+ uploadDataset(files: File[], address: string, metadata: DatasetMetadata, annotations: string[], epochs?: number): Promise<{
28
+ storageInfos: WalrusStorageInfo[];
29
+ transaction: Transaction;
30
+ }>;
31
+ private createDataset;
32
+ getDatasets(ownerAddress: string): Promise<{
33
+ id: string | undefined;
34
+ name: any;
35
+ description: any;
36
+ dataType: any;
37
+ dataSize: any;
38
+ creator: any;
39
+ license: any;
40
+ tags: any;
41
+ }[]>;
42
+ getDatasetById(datasetId: string): Promise<{
43
+ id: string;
44
+ name: any;
45
+ description: any;
46
+ dataType: any;
47
+ dataSize: any;
48
+ creator: any;
49
+ license: any;
50
+ tags: any;
51
+ }>;
52
+ private uploadDatasetFiles;
53
+ private uploadMedia;
54
+ uploadTrainingData(file: File, address: string): Promise<WalrusStorageInfo>;
55
+ private transformResponse;
56
+ getTrainingData(blobIds: string[]): Promise<Blob[]>;
57
+ getMedia(blobId: string): Promise<Blob>;
58
+ }
@@ -0,0 +1,323 @@
1
+ // src/sdk.ts
2
+ import { SuiClient } from "@mysten/sui/client";
3
+ import { Transaction } from "@mysten/sui/transactions";
4
+ import { WalrusStorageStatus } from "./types.js";
5
+ export class OpenGraphClient {
6
+ constructor(config) {
7
+ this.walrusNetwork = "testnet";
8
+ this.walrusPublisherUrl = "https://publisher.testnet.walrus.atalma.io";
9
+ this.walrusAggregatorUrl = "https://aggregator.testnet.walrus.atalma.io";
10
+ this.networkUrl = config.networkUrl;
11
+ this.packageId = config.packageId;
12
+ this.gasBudget = config.gasBudget;
13
+ this.suiClient = new SuiClient({ url: config.networkUrl });
14
+ this.walrusNetwork = config.walrusNetwork ?? "testnet";
15
+ this.walrusPublisherUrl = config.walrusPublisherUrl ?? "https://publisher.testnet.walrus.atalma.io";
16
+ this.walrusAggregatorUrl = config.walrusAggregatorUrl ?? "https://aggregator.testnet.walrus.atalma.io";
17
+ }
18
+ // =============================
19
+ // Getter Methods
20
+ // =============================
21
+ getNetworkUrl() {
22
+ return this.networkUrl;
23
+ }
24
+ getPackageId() {
25
+ return this.packageId;
26
+ }
27
+ getGasBudget() {
28
+ return this.gasBudget;
29
+ }
30
+ getWalrusNetwork() {
31
+ return this.walrusNetwork;
32
+ }
33
+ getWalrusPublisherUrl() {
34
+ return this.walrusPublisherUrl;
35
+ }
36
+ getWalrusAggregatorUrl() {
37
+ return this.walrusAggregatorUrl;
38
+ }
39
+ getConfig() {
40
+ return {
41
+ networkUrl: this.networkUrl,
42
+ packageId: this.packageId,
43
+ gasBudget: this.gasBudget,
44
+ walrusNetwork: this.walrusNetwork,
45
+ walrusPublisherUrl: this.walrusPublisherUrl,
46
+ walrusAggregatorUrl: this.walrusAggregatorUrl,
47
+ };
48
+ }
49
+ // =============================
50
+ // Setter Methods
51
+ // =============================
52
+ setNetworkUrl(url) {
53
+ this.networkUrl = url;
54
+ this.suiClient = new SuiClient({ url }); // 변경 시 client 재생성
55
+ }
56
+ setPackageId(packageId) {
57
+ this.packageId = packageId;
58
+ }
59
+ setGasBudget(gasBudget) {
60
+ this.gasBudget = gasBudget;
61
+ }
62
+ setWalrusNetwork(network) {
63
+ this.walrusNetwork = network;
64
+ }
65
+ setWalrusPublisherUrl(url) {
66
+ this.walrusPublisherUrl = url;
67
+ }
68
+ setWalrusAggregatorUrl(url) {
69
+ this.walrusAggregatorUrl = url;
70
+ }
71
+ // Utility
72
+ getSuiScanUrl(type, id) {
73
+ const baseUrl = `https://suiscan.xyz/${this.walrusNetwork}`;
74
+ if (type === "transaction") {
75
+ return `${baseUrl}/tx/${id}`;
76
+ }
77
+ else if (type === "account") {
78
+ return `${baseUrl}/account/${id}`;
79
+ }
80
+ else {
81
+ return `${baseUrl}/object/${id}`;
82
+ }
83
+ }
84
+ // walrus and create ptb tx
85
+ async uploadDataset(files, address, metadata, annotations, epochs) {
86
+ const storageInfos = await this.uploadDatasetFiles(files, address, epochs);
87
+ const dataFiles = await Promise.all(storageInfos.map(async (info, i) => {
88
+ const file = files[i];
89
+ const arrayBuffer = await file.arrayBuffer();
90
+ const hashBuffer = await crypto.subtle.digest("SHA-256", arrayBuffer);
91
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
92
+ const fileHash = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
93
+ return {
94
+ blobId: info.blobId,
95
+ fileHash,
96
+ };
97
+ }));
98
+ const tx = await this.createDataset(address, metadata, annotations, dataFiles);
99
+ return {
100
+ storageInfos,
101
+ transaction: tx,
102
+ };
103
+ }
104
+ // create ptb
105
+ async createDataset(accountAddress, metadata, annotations, files) {
106
+ const tx = new Transaction();
107
+ tx.setGasBudget(this.gasBudget);
108
+ const metadataObject = tx.moveCall({
109
+ target: `${this.packageId}::metadata::new_metadata`,
110
+ arguments: [
111
+ tx.pure.option("string", metadata.description),
112
+ tx.pure.string(metadata.dataType),
113
+ tx.pure.u64(BigInt(metadata.dataSize)),
114
+ tx.pure.option("string", metadata.creator),
115
+ tx.pure.option("string", metadata.license),
116
+ tx.pure.option("vector<string>", metadata.tags),
117
+ ],
118
+ });
119
+ const dataset = tx.moveCall({
120
+ target: `${this.packageId}::dataset::new_dataset`,
121
+ arguments: [tx.pure.string(metadata.name), metadataObject],
122
+ });
123
+ for (let i = 0; i < files.length; i++) {
124
+ const rangeOptionObject = tx.moveCall({
125
+ target: `${this.packageId}::dataset::new_range_option`,
126
+ arguments: [tx.pure.option("u64", null), tx.pure.option("u64", null)],
127
+ });
128
+ const dataObject = tx.moveCall({
129
+ target: `${this.packageId}::dataset::new_data`,
130
+ arguments: [
131
+ tx.pure.string(`data_${i}`),
132
+ tx.pure.string(files[i].blobId),
133
+ tx.pure.string(files[i].fileHash),
134
+ rangeOptionObject,
135
+ ],
136
+ });
137
+ tx.moveCall({
138
+ target: `${this.packageId}::dataset::add_annotation_label`,
139
+ arguments: [dataObject, tx.pure.string(annotations[i])],
140
+ });
141
+ tx.moveCall({
142
+ target: `${this.packageId}::dataset::add_data`,
143
+ arguments: [dataset, dataObject],
144
+ });
145
+ }
146
+ tx.transferObjects([dataset], accountAddress);
147
+ return tx;
148
+ }
149
+ async getDatasets(ownerAddress) {
150
+ if (!ownerAddress) {
151
+ throw new Error("OwnerAddress ID is required");
152
+ }
153
+ const { data } = await this.suiClient.getOwnedObjects({ owner: ownerAddress });
154
+ const objectIds = data
155
+ .map(d => d.data?.objectId)
156
+ .filter((id) => id !== undefined);
157
+ const objects = await this.suiClient.multiGetObjects({
158
+ ids: objectIds,
159
+ options: { showContent: true, showType: true },
160
+ });
161
+ return objects
162
+ .filter(obj => obj.data?.content?.dataType === "moveObject" &&
163
+ obj.data?.content?.type?.includes("dataset::Dataset"))
164
+ .map(obj => {
165
+ const content = obj.data?.content;
166
+ return {
167
+ id: obj.data?.objectId,
168
+ name: content.fields.name,
169
+ description: content.fields.description,
170
+ dataType: content.fields.data_type,
171
+ dataSize: content.fields.data_size,
172
+ creator: content.fields.creator,
173
+ license: content.fields.license,
174
+ tags: content.fields.tags,
175
+ };
176
+ });
177
+ }
178
+ async getDatasetById(datasetId) {
179
+ if (!datasetId) {
180
+ throw new Error("Dataset ID is required");
181
+ }
182
+ const object = await this.suiClient.getObject({
183
+ id: datasetId,
184
+ options: { showContent: true, showType: true },
185
+ });
186
+ if (object.data?.content?.dataType !== "moveObject") {
187
+ throw new Error("Invalid dataset object");
188
+ }
189
+ const content = object.data?.content;
190
+ return {
191
+ id: object.data?.objectId,
192
+ name: content.fields.name,
193
+ description: content.fields.description,
194
+ dataType: content.fields.data_type,
195
+ dataSize: content.fields.data_size,
196
+ creator: content.fields.creator,
197
+ license: content.fields.license,
198
+ tags: content.fields.tags,
199
+ };
200
+ }
201
+ // walrus
202
+ async uploadDatasetFiles(files, address, epochs) {
203
+ const uploadPromises = files.map(file => this.uploadMedia(file, address, epochs));
204
+ return await Promise.all(uploadPromises);
205
+ }
206
+ // 미디어 업로드
207
+ async uploadMedia(file, sendTo, epochs) {
208
+ try {
209
+ let epochsParam = epochs ? `&epochs=${epochs}` : "";
210
+ const url = `${this.walrusPublisherUrl}/v1/blobs?send_object_to=${sendTo}${epochsParam}`;
211
+ const response = await fetch(url, {
212
+ method: "PUT",
213
+ headers: {
214
+ "Content-Type": file.type,
215
+ "Content-Length": file.size.toString(),
216
+ },
217
+ body: file,
218
+ });
219
+ if (!response.ok) {
220
+ const errorText = await response.text();
221
+ throw new Error(`업로드 실패: ${response.status} ${response.statusText} - ${errorText}`);
222
+ }
223
+ const data = await response.json();
224
+ let storageInfo;
225
+ if ("alreadyCertified" in data) {
226
+ storageInfo = {
227
+ status: WalrusStorageStatus.ALREADY_CERTIFIED,
228
+ blobId: data.alreadyCertified.blobId,
229
+ endEpoch: data.alreadyCertified.endEpoch,
230
+ suiRefType: "Previous Sui Certified Event",
231
+ suiRef: data.alreadyCertified.event.txDigest,
232
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.alreadyCertified.blobId}`,
233
+ suiScanUrl: this.getSuiScanUrl("transaction", data.alreadyCertified.event.txDigest),
234
+ suiRefId: data.alreadyCertified.event.txDigest,
235
+ };
236
+ }
237
+ else if ("newlyCreated" in data) {
238
+ storageInfo = {
239
+ status: WalrusStorageStatus.NEWLY_CREATED,
240
+ blobId: data.newlyCreated.blobObject.blobId,
241
+ endEpoch: data.newlyCreated.blobObject.storage.endEpoch,
242
+ suiRefType: "Associated Sui Object",
243
+ suiRef: data.newlyCreated.blobObject.id,
244
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.newlyCreated.blobObject.blobId}`,
245
+ suiScanUrl: this.getSuiScanUrl("object", data.newlyCreated.blobObject.id),
246
+ suiRefId: data.newlyCreated.blobObject.id,
247
+ };
248
+ }
249
+ else {
250
+ throw new Error("알 수 없는 응답 형식");
251
+ }
252
+ return storageInfo;
253
+ }
254
+ catch (error) {
255
+ console.error("미디어 업로드 오류:", error);
256
+ throw new Error(`미디어 업로드 실패: ${error instanceof Error ? error.message : "알 수 없는 오류"}`);
257
+ }
258
+ }
259
+ async uploadTrainingData(file, address) {
260
+ const response = await fetch(`${this.walrusPublisherUrl}/v1/blobs?send_object_to=${address}`, {
261
+ method: "PUT",
262
+ body: file,
263
+ });
264
+ if (!response.ok) {
265
+ throw new Error(`Failed to upload file: ${response.statusText}`);
266
+ }
267
+ const data = await response.json();
268
+ return this.transformResponse(data);
269
+ }
270
+ transformResponse(data) {
271
+ if ("alreadyCertified" in data) {
272
+ return {
273
+ status: WalrusStorageStatus.ALREADY_CERTIFIED,
274
+ blobId: data.alreadyCertified.blobId,
275
+ endEpoch: data.alreadyCertified.endEpoch,
276
+ suiRefType: "Previous Sui Certified Event",
277
+ suiRef: data.alreadyCertified.event.txDigest,
278
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.alreadyCertified.blobId}`,
279
+ suiScanUrl: this.getSuiScanUrl("transaction", data.alreadyCertified.event.txDigest),
280
+ suiRefId: data.alreadyCertified.event.txDigest,
281
+ };
282
+ }
283
+ else if ("newlyCreated" in data) {
284
+ return {
285
+ status: WalrusStorageStatus.NEWLY_CREATED,
286
+ blobId: data.newlyCreated.blobObject.blobId,
287
+ endEpoch: data.newlyCreated.blobObject.storage.endEpoch,
288
+ suiRefType: "Associated Sui Object",
289
+ suiRef: data.newlyCreated.blobObject.id,
290
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.newlyCreated.blobObject.blobId}`,
291
+ suiScanUrl: this.getSuiScanUrl("object", data.newlyCreated.blobObject.id),
292
+ suiRefId: data.newlyCreated.blobObject.id,
293
+ };
294
+ }
295
+ else {
296
+ throw new Error("Unknown response format");
297
+ }
298
+ }
299
+ async getTrainingData(blobIds) {
300
+ try {
301
+ const getPromises = blobIds.map(blobId => this.getMedia(blobId));
302
+ return await Promise.all(getPromises);
303
+ }
304
+ catch (error) {
305
+ console.error("학습 데이터 가져오기 오류:", error);
306
+ throw error;
307
+ }
308
+ }
309
+ async getMedia(blobId) {
310
+ try {
311
+ const url = `${this.walrusAggregatorUrl}/v1/blobs/${blobId}`;
312
+ const response = await fetch(url);
313
+ if (!response.ok) {
314
+ throw new Error(`미디어 가져오기 실패: ${response.status} ${response.statusText}`);
315
+ }
316
+ return await response.blob();
317
+ }
318
+ catch (error) {
319
+ console.error("미디어 가져오기 오류:", error);
320
+ throw error;
321
+ }
322
+ }
323
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./dataset.js";
2
+ export * from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./dataset.js";
2
+ export * from "./types.js";
@@ -0,0 +1,32 @@
1
+ export type OpenGraphClientConfig = {
2
+ networkUrl: string;
3
+ packageId: string;
4
+ gasBudget: number;
5
+ walrusNetwork?: string;
6
+ walrusPublisherUrl?: string;
7
+ walrusAggregatorUrl?: string;
8
+ };
9
+ export interface DatasetMetadata {
10
+ name: string;
11
+ description?: string;
12
+ tags?: string[];
13
+ dataType: string;
14
+ dataSize: number;
15
+ creator?: string;
16
+ license?: string;
17
+ }
18
+ export declare enum WalrusStorageStatus {
19
+ ALREADY_CERTIFIED = "Already certified",
20
+ NEWLY_CREATED = "Newly created",
21
+ UNKNOWN = "Unknown"
22
+ }
23
+ export interface WalrusStorageInfo {
24
+ blobId: string;
25
+ endEpoch: number;
26
+ status: WalrusStorageStatus;
27
+ suiRef: string;
28
+ suiRefType: string;
29
+ mediaUrl: string;
30
+ suiScanUrl: string;
31
+ suiRefId: string;
32
+ }
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ // src/types.ts
2
+ export var WalrusStorageStatus;
3
+ (function (WalrusStorageStatus) {
4
+ WalrusStorageStatus["ALREADY_CERTIFIED"] = "Already certified";
5
+ WalrusStorageStatus["NEWLY_CREATED"] = "Newly created";
6
+ WalrusStorageStatus["UNKNOWN"] = "Unknown";
7
+ })(WalrusStorageStatus || (WalrusStorageStatus = {}));
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "tensorflow-sui2",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc"
8
+ },
9
+ "dependencies": {
10
+ "@mysten/sui": "^1.27.0",
11
+ "@mysten/sui.js": "^0.44.0"
12
+ },
13
+ "devDependencies": {
14
+ "typescript": "^5.3.0"
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "ISC",
19
+ "description": ""
20
+ }
package/src/dataset.ts ADDED
@@ -0,0 +1,388 @@
1
+ // src/sdk.ts
2
+ import { SuiClient } from "@mysten/sui/client";
3
+ import { Transaction } from "@mysten/sui/transactions";
4
+ import { OpenGraphClientConfig, DatasetMetadata, WalrusStorageInfo, WalrusStorageStatus } from "./types.js";
5
+
6
+ export class OpenGraphClient {
7
+ private networkUrl: string;
8
+ private packageId: string;
9
+ private gasBudget: number;
10
+ public suiClient: SuiClient;
11
+
12
+ private walrusNetwork = "testnet";
13
+ private walrusPublisherUrl = "https://publisher.testnet.walrus.atalma.io";
14
+ private walrusAggregatorUrl = "https://aggregator.testnet.walrus.atalma.io";
15
+
16
+ constructor(config: OpenGraphClientConfig) {
17
+ this.networkUrl = config.networkUrl;
18
+ this.packageId = config.packageId;
19
+ this.gasBudget = config.gasBudget;
20
+ this.suiClient = new SuiClient({ url: config.networkUrl });
21
+
22
+ this.walrusNetwork = config.walrusNetwork ?? "testnet";
23
+ this.walrusPublisherUrl = config.walrusPublisherUrl ?? "https://publisher.testnet.walrus.atalma.io";
24
+ this.walrusAggregatorUrl = config.walrusAggregatorUrl ?? "https://aggregator.testnet.walrus.atalma.io";
25
+ }
26
+
27
+ // =============================
28
+ // Getter Methods
29
+ // =============================
30
+ public getNetworkUrl(): string {
31
+ return this.networkUrl;
32
+ }
33
+
34
+ public getPackageId(): string {
35
+ return this.packageId;
36
+ }
37
+
38
+ public getGasBudget(): number {
39
+ return this.gasBudget;
40
+ }
41
+
42
+ public getWalrusNetwork(): string {
43
+ return this.walrusNetwork;
44
+ }
45
+
46
+ public getWalrusPublisherUrl(): string {
47
+ return this.walrusPublisherUrl;
48
+ }
49
+
50
+ public getWalrusAggregatorUrl(): string {
51
+ return this.walrusAggregatorUrl;
52
+ }
53
+
54
+ public getConfig(): OpenGraphClientConfig {
55
+ return {
56
+ networkUrl: this.networkUrl,
57
+ packageId: this.packageId,
58
+ gasBudget: this.gasBudget,
59
+ walrusNetwork: this.walrusNetwork,
60
+ walrusPublisherUrl: this.walrusPublisherUrl,
61
+ walrusAggregatorUrl: this.walrusAggregatorUrl,
62
+ };
63
+ }
64
+
65
+ // =============================
66
+ // Setter Methods
67
+ // =============================
68
+ public setNetworkUrl(url: string): void {
69
+ this.networkUrl = url;
70
+ this.suiClient = new SuiClient({ url }); // 변경 시 client 재생성
71
+ }
72
+
73
+ public setPackageId(packageId: string): void {
74
+ this.packageId = packageId;
75
+ }
76
+
77
+ public setGasBudget(gasBudget: number): void {
78
+ this.gasBudget = gasBudget;
79
+ }
80
+
81
+ public setWalrusNetwork(network: string) {
82
+ this.walrusNetwork = network;
83
+ }
84
+
85
+ public setWalrusPublisherUrl(url: string) {
86
+ this.walrusPublisherUrl = url;
87
+ }
88
+
89
+ public setWalrusAggregatorUrl(url: string) {
90
+ this.walrusAggregatorUrl = url;
91
+ }
92
+
93
+ // Utility
94
+ private getSuiScanUrl(type: "transaction" | "object" | "account", id: string) {
95
+ const baseUrl = `https://suiscan.xyz/${this.walrusNetwork}`;
96
+ if (type === "transaction") {
97
+ return `${baseUrl}/tx/${id}`;
98
+ } else if (type === "account") {
99
+ return `${baseUrl}/account/${id}`;
100
+ } else {
101
+ return `${baseUrl}/object/${id}`;
102
+ }
103
+ }
104
+
105
+ // walrus and create ptb tx
106
+ public async uploadDataset(
107
+ files: File[],
108
+ address: string,
109
+ metadata: DatasetMetadata,
110
+ annotations: string[],
111
+ epochs?: number
112
+ ) {
113
+ const storageInfos = await this.uploadDatasetFiles(files, address, epochs);
114
+
115
+ const dataFiles = await Promise.all(
116
+ storageInfos.map(async (info, i) => {
117
+ const file = files[i];
118
+ const arrayBuffer = await file.arrayBuffer();
119
+ const hashBuffer = await crypto.subtle.digest("SHA-256", arrayBuffer);
120
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
121
+ const fileHash = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
122
+
123
+ return {
124
+ blobId: info.blobId,
125
+ fileHash,
126
+ };
127
+ })
128
+ );
129
+
130
+ const tx = await this.createDataset(address, metadata, annotations, dataFiles);
131
+
132
+ return {
133
+ storageInfos,
134
+ transaction: tx,
135
+ };
136
+ }
137
+
138
+ // create ptb
139
+ private async createDataset(
140
+ accountAddress: string,
141
+ metadata: DatasetMetadata,
142
+ annotations: string[],
143
+ files: { blobId: string; fileHash: string }[]
144
+ ) {
145
+ const tx = new Transaction();
146
+ tx.setGasBudget(this.gasBudget);
147
+
148
+ const metadataObject = tx.moveCall({
149
+ target: `${this.packageId}::metadata::new_metadata`,
150
+ arguments: [
151
+ tx.pure.option("string", metadata.description),
152
+ tx.pure.string(metadata.dataType),
153
+ tx.pure.u64(BigInt(metadata.dataSize)),
154
+ tx.pure.option("string", metadata.creator),
155
+ tx.pure.option("string", metadata.license),
156
+ tx.pure.option("vector<string>", metadata.tags),
157
+ ],
158
+ });
159
+
160
+ const dataset = tx.moveCall({
161
+ target: `${this.packageId}::dataset::new_dataset`,
162
+ arguments: [tx.pure.string(metadata.name), metadataObject],
163
+ });
164
+
165
+ for (let i = 0; i < files.length; i++) {
166
+ const rangeOptionObject = tx.moveCall({
167
+ target: `${this.packageId}::dataset::new_range_option`,
168
+ arguments: [tx.pure.option("u64", null), tx.pure.option("u64", null)],
169
+ });
170
+
171
+ const dataObject = tx.moveCall({
172
+ target: `${this.packageId}::dataset::new_data`,
173
+ arguments: [
174
+ tx.pure.string(`data_${i}`),
175
+ tx.pure.string(files[i].blobId),
176
+ tx.pure.string(files[i].fileHash),
177
+ rangeOptionObject,
178
+ ],
179
+ });
180
+
181
+ tx.moveCall({
182
+ target: `${this.packageId}::dataset::add_annotation_label`,
183
+ arguments: [dataObject, tx.pure.string(annotations[i])],
184
+ });
185
+
186
+ tx.moveCall({
187
+ target: `${this.packageId}::dataset::add_data`,
188
+ arguments: [dataset, dataObject],
189
+ });
190
+ }
191
+
192
+ tx.transferObjects([dataset], accountAddress);
193
+ return tx;
194
+ }
195
+
196
+ public async getDatasets(ownerAddress: string) {
197
+ if (!ownerAddress) {
198
+ throw new Error("OwnerAddress ID is required");
199
+ }
200
+
201
+ const { data } = await this.suiClient.getOwnedObjects({ owner: ownerAddress });
202
+ const objectIds = data
203
+ .map(d => d.data?.objectId)
204
+ .filter((id): id is string => id !== undefined);
205
+
206
+ const objects = await this.suiClient.multiGetObjects({
207
+ ids: objectIds,
208
+ options: { showContent: true, showType: true },
209
+ });
210
+
211
+ return objects
212
+ .filter(obj =>
213
+ obj.data?.content?.dataType === "moveObject" &&
214
+ obj.data?.content?.type?.includes("dataset::Dataset"))
215
+ .map(obj => {
216
+ const content = obj.data?.content as any;
217
+ return {
218
+ id: obj.data?.objectId,
219
+ name: content.fields.name,
220
+ description: content.fields.description,
221
+ dataType: content.fields.data_type,
222
+ dataSize: content.fields.data_size,
223
+ creator: content.fields.creator,
224
+ license: content.fields.license,
225
+ tags: content.fields.tags,
226
+ };
227
+ });
228
+ }
229
+
230
+ public async getDatasetById(datasetId: string) {
231
+ if (!datasetId) {
232
+ throw new Error("Dataset ID is required");
233
+ }
234
+
235
+ const object = await this.suiClient.getObject({
236
+ id: datasetId,
237
+ options: { showContent: true, showType: true },
238
+ });
239
+
240
+ if (object.data?.content?.dataType !== "moveObject") {
241
+ throw new Error("Invalid dataset object");
242
+ }
243
+
244
+ const content = object.data?.content as any;
245
+ return {
246
+ id: object.data?.objectId,
247
+ name: content.fields.name,
248
+ description: content.fields.description,
249
+ dataType: content.fields.data_type,
250
+ dataSize: content.fields.data_size,
251
+ creator: content.fields.creator,
252
+ license: content.fields.license,
253
+ tags: content.fields.tags,
254
+ };
255
+ }
256
+
257
+ // walrus
258
+ private async uploadDatasetFiles(files: File[], address: string, epochs?: number): Promise<WalrusStorageInfo[]> {
259
+ const uploadPromises = files.map(file => this.uploadMedia(file, address, epochs));
260
+ return await Promise.all(uploadPromises);
261
+ }
262
+
263
+ // 미디어 업로드
264
+ private async uploadMedia(file: File, sendTo: string, epochs?: number): Promise<WalrusStorageInfo> {
265
+ try {
266
+ let epochsParam = epochs ? `&epochs=${epochs}` : "";
267
+ const url = `${this.walrusPublisherUrl}/v1/blobs?send_object_to=${sendTo}${epochsParam}`;
268
+
269
+ const response = await fetch(url, {
270
+ method: "PUT",
271
+ headers: {
272
+ "Content-Type": file.type,
273
+ "Content-Length": file.size.toString(),
274
+ },
275
+ body: file,
276
+ });
277
+
278
+ if (!response.ok) {
279
+ const errorText = await response.text();
280
+ throw new Error(`업로드 실패: ${response.status} ${response.statusText} - ${errorText}`);
281
+ }
282
+
283
+ const data = await response.json();
284
+ let storageInfo: WalrusStorageInfo;
285
+
286
+ if ("alreadyCertified" in data) {
287
+ storageInfo = {
288
+ status: WalrusStorageStatus.ALREADY_CERTIFIED,
289
+ blobId: data.alreadyCertified.blobId,
290
+ endEpoch: data.alreadyCertified.endEpoch,
291
+ suiRefType: "Previous Sui Certified Event",
292
+ suiRef: data.alreadyCertified.event.txDigest,
293
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.alreadyCertified.blobId}`,
294
+ suiScanUrl: this.getSuiScanUrl("transaction", data.alreadyCertified.event.txDigest),
295
+ suiRefId: data.alreadyCertified.event.txDigest,
296
+ };
297
+ } else if ("newlyCreated" in data) {
298
+ storageInfo = {
299
+ status: WalrusStorageStatus.NEWLY_CREATED,
300
+ blobId: data.newlyCreated.blobObject.blobId,
301
+ endEpoch: data.newlyCreated.blobObject.storage.endEpoch,
302
+ suiRefType: "Associated Sui Object",
303
+ suiRef: data.newlyCreated.blobObject.id,
304
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.newlyCreated.blobObject.blobId}`,
305
+ suiScanUrl: this.getSuiScanUrl("object", data.newlyCreated.blobObject.id),
306
+ suiRefId: data.newlyCreated.blobObject.id,
307
+ };
308
+ } else {
309
+ throw new Error("알 수 없는 응답 형식");
310
+ }
311
+
312
+ return storageInfo;
313
+ } catch (error) {
314
+ console.error("미디어 업로드 오류:", error);
315
+ throw new Error(
316
+ `미디어 업로드 실패: ${error instanceof Error ? error.message : "알 수 없는 오류"}`
317
+ );
318
+ }
319
+ }
320
+
321
+ public async uploadTrainingData(file: File, address: string): Promise<WalrusStorageInfo> {
322
+ const response = await fetch(`${this.walrusPublisherUrl}/v1/blobs?send_object_to=${address}`, {
323
+ method: "PUT",
324
+ body: file,
325
+ });
326
+
327
+ if (!response.ok) {
328
+ throw new Error(`Failed to upload file: ${response.statusText}`);
329
+ }
330
+
331
+ const data = await response.json();
332
+ return this.transformResponse(data);
333
+ }
334
+
335
+ private transformResponse(data: any): WalrusStorageInfo {
336
+ if ("alreadyCertified" in data) {
337
+ return {
338
+ status: WalrusStorageStatus.ALREADY_CERTIFIED,
339
+ blobId: data.alreadyCertified.blobId,
340
+ endEpoch: data.alreadyCertified.endEpoch,
341
+ suiRefType: "Previous Sui Certified Event",
342
+ suiRef: data.alreadyCertified.event.txDigest,
343
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.alreadyCertified.blobId}`,
344
+ suiScanUrl: this.getSuiScanUrl("transaction", data.alreadyCertified.event.txDigest),
345
+ suiRefId: data.alreadyCertified.event.txDigest,
346
+ };
347
+ } else if ("newlyCreated" in data) {
348
+ return {
349
+ status: WalrusStorageStatus.NEWLY_CREATED,
350
+ blobId: data.newlyCreated.blobObject.blobId,
351
+ endEpoch: data.newlyCreated.blobObject.storage.endEpoch,
352
+ suiRefType: "Associated Sui Object",
353
+ suiRef: data.newlyCreated.blobObject.id,
354
+ mediaUrl: `${this.walrusAggregatorUrl}/v1/blobs/${data.newlyCreated.blobObject.blobId}`,
355
+ suiScanUrl: this.getSuiScanUrl("object", data.newlyCreated.blobObject.id),
356
+ suiRefId: data.newlyCreated.blobObject.id,
357
+ };
358
+ } else {
359
+ throw new Error("Unknown response format");
360
+ }
361
+ }
362
+
363
+ public async getTrainingData(blobIds: string[]): Promise<Blob[]> {
364
+ try {
365
+ const getPromises = blobIds.map(blobId => this.getMedia(blobId));
366
+ return await Promise.all(getPromises);
367
+ } catch (error) {
368
+ console.error("학습 데이터 가져오기 오류:", error);
369
+ throw error;
370
+ }
371
+ }
372
+
373
+ public async getMedia(blobId: string): Promise<Blob> {
374
+ try {
375
+ const url = `${this.walrusAggregatorUrl}/v1/blobs/${blobId}`;
376
+ const response = await fetch(url);
377
+
378
+ if (!response.ok) {
379
+ throw new Error(`미디어 가져오기 실패: ${response.status} ${response.statusText}`);
380
+ }
381
+
382
+ return await response.blob();
383
+ } catch (error) {
384
+ console.error("미디어 가져오기 오류:", error);
385
+ throw error;
386
+ }
387
+ }
388
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./dataset.js";
2
+ export * from "./types.js";
package/src/types.ts ADDED
@@ -0,0 +1,37 @@
1
+ // src/types.ts
2
+
3
+ export type OpenGraphClientConfig = {
4
+ networkUrl: string;
5
+ packageId: string;
6
+ gasBudget: number;
7
+ walrusNetwork?: string;
8
+ walrusPublisherUrl?: string;
9
+ walrusAggregatorUrl?: string;
10
+ };
11
+
12
+ export interface DatasetMetadata {
13
+ name: string;
14
+ description?: string;
15
+ tags?: string[];
16
+ dataType: string;
17
+ dataSize: number;
18
+ creator?: string;
19
+ license?: string;
20
+ }
21
+
22
+ export enum WalrusStorageStatus {
23
+ ALREADY_CERTIFIED = "Already certified",
24
+ NEWLY_CREATED = "Newly created",
25
+ UNKNOWN = "Unknown",
26
+ }
27
+
28
+ export interface WalrusStorageInfo {
29
+ blobId: string;
30
+ endEpoch: number;
31
+ status: WalrusStorageStatus;
32
+ suiRef: string;
33
+ suiRefType: string;
34
+ mediaUrl: string;
35
+ suiScanUrl: string;
36
+ suiRefId: string;
37
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "declaration": true,
6
+ "outDir": "./dist",
7
+ "moduleResolution": "Node",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "skipLibCheck": true
13
+ },
14
+ }