> ## 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을 저장할 스토리지 epoch 수 (기본값: 1)
</ParamField>

<ParamField query="send_object_to" optional>
  Blob 오브젝트를 전송할 Sui 주소
</ParamField>

<ParamField query="deletable" optional>
  Blob을 삭제 가능하게 저장할지 여부 (기본값: false)
</ParamField>

### 요청 예시

<RequestExample>
  ```bash Bash theme={null}
  # 문자열을 1 epoch 동안 저장
  curl -X PUT "https://walrus-mainnet-publisher.nami.cloud/${endpoint_key}/v1/blobs" \
    -d "some string"

  # 파일을 5 epoch 동안 저장
  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 epoch 동안 저장
  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 epoch 동안 저장
  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 epoch 동안 저장
  	})
  	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("유효 만료 epoch: %d\n", resp.AlreadyCertified.EndEpoch)
  	}

  	// 예시 2: 파일 저장
  	filePath := "example.txt"
  	fileResp, err := client.StoreFile(filePath, &walrus.StoreOptions{
  		Epochs: 10, // 10 epoch 동안 저장
  	})
  	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 이벤트 ID를 반환하며, 이를 통해 Sui explorer 또는 Sui SDK를 사용하여 Sui Blob 오브젝트를 생성한 트랜잭션을 찾을 수 있습니다.
