> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nami.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# BLOBの保存

> Walrus Publisherによるデータ保存、ストレージ料金の支払い、Walrusネットワークとのトランザクション管理

<Note>
  Walrus Publisherは、データの保存準備、ストレージ料金の支払い、およびWalrusネットワークとのトランザクション管理を担当します。
</Note>

## BLOBの保存

BLOBをWalrusネットワークに追加します。BLOBデータはリクエストボディで送信されます。Publisherがデータの保存準備、ストレージ料金の支払い、Walrusネットワークとのトランザクション管理を行います。

### ベースURL

```bash theme={null}
https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/
```

ここで`${endpoint_key}`は、あなた専用のエンドポイントキーです。

### クエリパラメータ

<ParamField query="epochs" optional>
  BLOBを保存するストレージエポック数（デフォルト: 1）
</ParamField>

<ParamField query="send_object_to" optional>
  BLOBオブジェクトを送信するSuiアドレス
</ParamField>

<ParamField query="deletable" optional>
  BLOBを削除可能として保存するかどうか（デフォルト: false）
</ParamField>

### リクエスト例

<RequestExample>
  ```bash Bash theme={null}
  # 1エポック分のストレージで文字列を保存
  curl -X PUT "https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs" \
    -d "some string"

  # 5エポック分のストレージでファイルを保存
  curl -X PUT "https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs?epochs=5" \
    --upload-file "some/file"

  # ファイルを保存し、BLOBオブジェクトを特定のアドレスに送信
  curl -X PUT "https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs?send_object_to=${ADDRESS}" \
    --upload-file "some/file"

  # ファイルを削除可能なBLOBとして保存
  curl -X PUT "https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs?deletable=true" \
    --upload-file "some/file"
  ```

  ```python Python theme={null}
  import requests

  # エンドポイントキーを設定
  endpoint_key = "your_endpoint_key"
  publisher_url = f"https://walrus-mainnet-publisher.nami.cloud/{endpoint_key}/v1/blobs"

  # 文字列を保存
  response = requests.put(publisher_url, data="some string")
  print(response.json())

  # 5エポック分のファイルを保存
  with open("some/file", "rb") as file:
      response = requests.put(f"{publisher_url}?epochs=5", data=file)
      print(response.json())
  ```

  ```javascript Node.js theme={null}
  // 文字列を保存
  fetch(`https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs`, {
    method: 'PUT',
    body: 'some string'
  })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

  // 5エポック分のファイルを保存
  const fs = require('fs');
  const fileData = fs.readFileSync('some/file');
  fetch(`https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs?epochs=5`, {
    method: 'PUT',
    body: fileData
  })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
  ```

  ```go Golang theme={null}
  package main

  import (
  	"fmt"
  	"log"
  	"os"

  	"github.com/namihq/walrus-go"
  )

  func main() {
  	// デフォルトエンドポイントで新しいクライアントを作成
  	client := walrus.NewClient()

  	// またはカスタムエンドポイントを指定
  	// client := walrus.NewClient(
  	//     walrus.WithPublisherURLs([]string{"https://walrus-mainnet-publisher.nami.cloud/your-endpoint-key"}),
  	// )

  	// 例1: バイト配列を保存
  	data := []byte("Hello, Walrus!")
  	resp, err := client.Store(data, &walrus.StoreOptions{
  		Epochs: 5, // 5エポック保存
  	})
  	if err != nil {
  		log.Fatalf("データ保存エラー: %v", err)
  	}
  	
  	// blobIdは後でデータ取得に利用可能
  	if resp.NewlyCreated != nil {
  		fmt.Printf("新しいBLOBを保存しました。ID: %s\n", resp.NewlyCreated.BlobObject.BlobId)
  		fmt.Printf("ストレージコスト: %d\n", resp.NewlyCreated.Cost)
  	} else if resp.AlreadyCertified != nil {
  		fmt.Printf("既存のBLOBが存在します。ID: %s\n", resp.AlreadyCertified.BlobId)
  		fmt.Printf("有効期限エポック: %d\n", resp.AlreadyCertified.EndEpoch)
  	}

  	// 例2: ファイルを保存
  	filePath := "example.txt"
  	fileResp, err := client.StoreFile(filePath, &walrus.StoreOptions{
  		Epochs: 10, // 10エポック保存
  	})
  	if err != nil {
  		log.Fatalf("ファイル保存エラー: %v", err)
  	}
  	
  	if fileResp.NewlyCreated != nil {
  		fmt.Printf("ファイルを保存しました。BLOB ID: %s\n", fileResp.NewlyCreated.BlobObject.BlobId)
  	}
  	
  	// 例3: URLから保存
  	urlResp, err := client.StoreFromURL("https://example.com/sample.jpg", &walrus.StoreOptions{
  		Epochs: 3,
  	})
  	if err != nil {
  		log.Fatalf("URLからの保存エラー: %v", err)
  	}
  	
  	if urlResp.NewlyCreated != nil {
  		fmt.Printf("URLコンテンツを保存しました。BLOB ID: %s\n", urlResp.NewlyCreated.BlobObject.BlobId)
  	}
  	
  	// 例4: 暗号化して保存
  	encryptedResp, err := client.Store([]byte("Secret data"), &walrus.StoreOptions{
  		Epochs: 5,
  		Encryption: &walrus.EncryptionOptions{
  			Key: []byte("a-32-byte-key-for-aes-256-encryption"), // AES-256用32バイト
  		},
  	})
  	if err != nil {
  		log.Fatalf("暗号化データ保存エラー: %v", err)
  	}
  	
  	if encryptedResp.NewlyCreated != nil {
  		fmt.Printf("暗号化データを保存しました。BLOB ID: %s\n", encryptedResp.NewlyCreated.BlobObject.BlobId)
  	}
  }
  ```
</RequestExample>

### レスポンス - 新規作成BLOB

BLOBが初めて保存された場合、`newlyCreated`フィールドに新しいBLOBの情報が含まれます:

```json theme={null}
{
  "newlyCreated": {
    "blobObject": {
      "id": "0xe91eee8c5b6f35b9a250cfc29e30f0d9e5463a21fd8d1ddb0fc22d44db4eac50",
      "registeredEpoch": 34,
      "blobId": "M4hsZGQ1oCktdzegB6HnI6Mi28S2nqOPHxK-W7_4BUk",
      "size": 17,
      "encodingType": "RS2",
      "certifiedEpoch": 34,
      "storage": {
        "id": "0x4748cd83217b5ce7aa77e7f1ad6fc5f7f694e26a157381b9391ac65c47815faf",
        "startEpoch": 34,
        "endEpoch": 35,
        "storageSize": 66034000
      },
      "deletable": false
    },
    "resourceOperation": {
      "registerFromScratch": {
        "encodedLength": 66034000,
        "epochsAhead": 1
      }
    },
    "cost": 132300
  }
}
```

### レスポンス - 既に認証済みのBLOB

Publisherが同じBLOB IDかつ有効期間が十分な認証済みBLOBを見つけた場合、`alreadyCertified` JSON構造が返されます:

```json theme={null}
{
  "alreadyCertified": {
    "blobId": "M4hsZGQ1oCktdzegB6HnI6Mi28S2nqOPHxK-W7_4BUk",
    "event": {
      "txDigest": "4XQHFa9S324wTzYHF3vsBSwpUZuLpmwTHYMFv9nsttSs",
      "eventSeq": "0"
    },
    "endEpoch": 35
  }
}
```

`event`フィールドは、Sui Blobオブジェクトを作成したトランザクションをSui ExplorerやSui SDKで検索するために利用できるSuiイベントIDを返します。
