Skip to main content

Command Palette

Search for a command to run...

EP 1: Web 3.0 Only... ด้วย Walrus และ Seal

สร้างแพลตฟอร์มคอนเทนต์แบบ OnlyFans ในโลก Web 3.0 ด้วย Walrus และ Seal

Updated
4 min read
EP 1: Web 3.0 Only... ด้วย Walrus และ Seal

สวัสดี ห่างหายกันไปหลายวัน ย้อนความไปนิดนึง จากความเดิมตอนที่แล้ว เราได้ทำการ Deploy website อย่างง่ายไปบน Walrus ไปแล้ว ยังจำได้ไหม ถ้าใครยังไม่ได้ลองเล่น ลองทำตามนี้ดูนะ https://onthemoveth.hashnode.dev/walrus-simple-usage

ในวันนี้เราจะไปให้ลึกขึ้นกว่าเดิม นั้นคือ….เราจะมาสอนทำ OnlyFans บน Web3 ด้วย Walrus + Seal หวังว่าคอนเทนต์จะไม่ติดเหลืองนะ :P (หลาย EP หน่อยนะ)

ลองแอบไปส่องกันได้ที่ https://only-fins.wal.app/

พร้อมแล้วไปลุยกันเลย

อย่างที่ทุกท่านทราบกันดีว่า… การเก็บข้อมูลขนาดใหญ่บน Blockchain นั้นทั้งแพงและช้าสุดๆ ด้วยเหตุนี้เอง Sui จึงได้พัฒนา Walrus ขึ้นมาเพื่อแก้ปัญหานี้โดย Walrus เป็น Decentralized Storage ที่เป็นมากกว่าแค่ ที่เก็บข้อมูล เพราะหัวใจสำคัญคือ ข้อมูลที่เก็บไว้สามารถนำไปใช้ใน Smart Contracts ได้โดยตรง!

แต่ปัญหาที่ตามมาคือ… ถึงแม้ข้อมูลจะอยู่บน Storage แบบกระจายศูนย์แล้ว แต่บน Blockchain ทุกคนสามารถเข้าถึงข้อมูลได้ แล้วเราจะสร้าง Content แบบ Exclusive ที่ให้สิทธิ์เฉพาะคนที่จ่ายเงินเข้ามาดูเท่านั้นได้ยังไง?

คำตอบก็คือ Seal 🔒 ครับ! ส่วน Seal คืออะไรนั้นไว้เรามาพูดกันในตอนถัดไปนะ

ในซีรีส์นี้ เราจะมาลงมือทำไปพร้อมๆ กัน ตั้งแต่การอัปโหลดไฟล์ไปที่ Walrus ไปจนถึงการใช้ Seal สร้างระบบ Subscription แบบ On-chain กันเลย!

ก่อนจะไปถึงขั้นตอนการสร้างระบบ Subscription สุดปัง เรามาเริ่มจากการทดลองอัปโหลดไฟล์ ลับ ของเราไปที่ Walrus Testnet กันก่อนดีกว่า

เริ่มจากการเตรียมเครื่องมือให้พร้อม

  1. ติดตั้ง Rust: เนื่องจาก Sui Binaries ถูกเขียนด้วย Rust เป็นหลัก ถ้ายังไม่มี ไปที่ rust-lang.org เเล้วทำตามขั้นนตอนการติดตั้ง rustup ได้เลย

  2. ติดตั้ง Sui CLI: เมื่อ Rust พร้อมแล้ว ก็ติดตั้ง Sui Command Line Interface (CLI) จาก GitHub ของ Mysten Labs ได้เลย เปิด Terminal หรือ Command Prompt แล้วรันคำสั่งนี้:

     cargo install --locked --git https://github.com/MystenLabs/sui.git --branch testnet sui
    

    ขั้นตอนนี้อาจใช้เวลาสักพักนะ เพราะมันจะดาวน์โหลดและคอมไพล์โค้ด

    • ตรวจสอบการติดตั้ง: พิมพ์ sui --version ถ้าเห็นเวอร์ชัน Sui CLI แสดงว่าเรียบร้อย!
  3. ตั้งค่า Sui Client และ Wallet

    • รัน sui client แล้วตอบ y เพื่อเชื่อมต่อกับ Sui Fullnode Server เลือก Testnet (หรือถ้าเคย setup แล้วให้รัน sui client switch --env testnet เพื่อเปลี่ยนไปใช้ testnet) ระบบจะสร้าง wallet address พร้อม recovery phrase อันนี้สำคัญมาก! เก็บรักษา Recovery Phrase ของคุณไว้ให้ดีๆ นะ ห้ามทำหายเด็ดขาด!

    • Export Private Key: สำหรับโค้ดของเรา เราจะใช้ private key เพื่อความสะดวกในการรันสคริปต์ ให้ทำตามนี้:

        # ก่อนอื่น ดู address ที่ใช้งานอยู่ของคุณ
        sui client active-address
      
        # จากนั้น export private key ออกมา
        sui keytool export --key-identity <your-address>
      

      เก็บ Private key ไว้นะเราจะต้องเอาไปใส่ใน env ในขั้นตอนถัดไป

    • หา Testnet SUI token เพื่อจ่ายค่า gas เราจะต้องมีเหรียญ SUI สำหรับค่า Gas ในการทำธุรกรรม ไปที่ Discord ของ Sui (ค้นหา Sui Discord) แล้วเข้าไปที่ช่อง #testnet-faucet พิมพ์ !faucet <YOUR_SUI_ADDRESS> (แทนที่ <YOUR_SUI_ADDRESS> ด้วยที่อยู่กระเป๋านั้นเอง)

    • ตรวจสอบยอดคงเหลือ: รัน sui client gas เพื่อดูว่าได้ SUI token เข้ากระเป๋าเราแล้วรึยัง

  4. ติดตั้ง Node.js และ TypeScript: ถ้ายังไม่มี Node.js และ npm ก็ติดตั้งให้เรียบร้อย (แนะนำเวอร์ชันล่าสุด) จากนั้นก็สร้างโปรเจกต์ TypeScript:

    ```bash

    1. Create and enter directory

    mkdir walrus-onlyfans && cd walrus-onlyfans

    2. Initialize package.json with ES module support

    npm init -y && npm pkg set type=module

    3. Install all dependencies in one command

    npm install @mysten/sui @mysten/walrus dotenv npm install --save-dev typescript @types/node tsx

4. Initialize TypeScript config

npx tsc --init


5. ติดตั้ง VS Code (แนะนำ): ดาวน์โหลด [Visual Studio Code](https://code.visualstudio.com/) แล้วติดตั้งส่วนขยาย **Move Analyzer** เพื่อความสะดวกในการเขียนโค้ด Move ([https://docs.sui.io/references/ide/move](https://docs.sui.io/references/ide/move))


เรียบร้อยถึงขั้นตอนนี้เราได้เตรียมเครื่องมือทุกอย่างเสร็จสิ้นเเล้ว พร้อมที่จะเริ่มขั้นตอนการเขียนโค๊ดละ

---

**พร้อมแล้ว มาเขียนโค้ดกัน!**

ก่อนอื่นสร้างไฟล์ `.env` ก่อนเพื่อเก็บข้อมูลอาทิเช่น Private key ร่วมถึง Network ที่จะใช้ทดสอบ

```plaintext
SUI_PRIVATE_KEY=suiprivkey1qqen0....
NETWORK=testnet

สร้างไฟล์ใหม่ชื่อ index.ts แล้วใส่โค้ดนี้ลงไปเพื่อตั้งค่า SuiClient และ WalrusClient และเตรียม keypair สำหรับลงชื่อธุรกรรม

import { getFullnodeUrl, SuiClient } from "@mysten/sui/client";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { walrus, WalrusFile } from "@mysten/walrus";
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
import { writeFileSync } from "fs";
import "dotenv/config";

const PRIVATE_KEY = process.env.SUI_PRIVATE_KEY;
const NETWORK = process.env.NETWORK || "testnet";
const WALRUS_UPLOAD_RELAY = "https://upload-relay.testnet.walrus.space";

async function main() {
    // 1) Init Sui + Walrus (upload ผ่าน relay เพื่อความเสถียร)
    const suiClient = new SuiClient({ url: getFullnodeUrl("testnet") });
    const walrusClient = suiClient.$extend(
        walrus({
            network: NETWORK,
            ไ: {
                host: WALRUS_UPLOAD_RELAY,
                sendTip: { max: 1_000 }, // หน่วย MIST
            },
        })
    );

    // 2) signer
    const { secretKey } = decodeSuiPrivateKey(PRIVATE_KEY);
    const keypair = Ed25519Keypair.fromSecretKey(secretKey);
    console.log(`กำลังใช้ Address: ${keypair.getPublicKey().toSuiAddress()}`);

    // 3) เตรียมเนื้อหา  
    const mySecretContent = "Test content Exclusive for onlyfans!";
    const file = WalrusFile.from({
        contents: new TextEncoder().encode(mySecretContent),
        identifier: "",
    });

    console.log("กำลังอัปโหลดไฟล์ไปที่ Walrus (ผ่าน relay)…");

    // 4) เขียนไฟล์ด้วย writeFiles (รองรับ relay/metadata/retry)
    const res = await walrusClient.walrus.writeFiles({
        files: [file],
        deletable: false,
        epochs: 1,
        signer: keypair,
    });
    const blobId = res[0].blobId;

    console.log(`อัปโหลดไฟล์สำเร็จ! Blob ID: ${blobId}`);

    // 5) รอให้ blob พร้อม แล้วอ่านข้อมูล
    console.log("\nกำลังอ่านไฟล์จาก Walrus...");
    const downloadedFile = await waitForBlobReady(walrusClient, blobId);

    // ✅ อ่านเป็นไบต์ แล้วถอดเป็น UTF-8 เอง (ไม่มี arrayBuffer())
    const u8 = await downloadedFile.bytes(); // Uint8Array
    const text = new TextDecoder("utf-8", { fatal: true }).decode(u8);

    // บันทึกเป็นไฟล์ข้อความ UTF-8 โดยตรง (กันอักขระเพี้ยน)
    const outputPath = "./downloaded-file.txt";
    writeFileSync(outputPath, text, { encoding: "utf8" });
    console.log(`✅ บันทึกไฟล์ไปที่: ${outputPath}`);
    console.log(`เนื้อหาไฟล์: "${text}"`);
}

async function waitForBlobReady(client: any, blobId: string, maxRetries = 10, delayMs = 5_000) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            const [file] = await client.walrus.getFiles({ ids: [blobId] });
            if (file) return file;
        } catch {
            console.error("Wait more...");
        }
        console.log(`⏳ Blob ยังไม่พร้อม (${i + 1}/${maxRetries}) รออีก ${delayMs / 1000}s...`);
        await new Promise((r) => setTimeout(r, delayMs));
    }
}

main().catch((e) => {
    console.error("❌ Error:", e?.message || e);
    process.exit(1);
});

พร้อมแล้วมารันโค๊ดกัน

เปิด Terminal ในโฟลเดอร์ walrus-onlyfans แล้วรันคำสั่งนี้ (ก่อนรันอย่าลืมหา sui testnet กับ wal testnet -> https://stake-wal.wal.app/?network=testnet มาก่อนด้วยนะ)

npx tsx index.ts

เพียงเท่านี้เราก็สามารถอัพโหลดไฟล์ของเราไปเก็บไว้บน Walrus ได้เเล้วละ ซึ่งสามารดูผลลัพธ์ได้โดยการนำ Blob ID ไปค้นหาใน https://walruscan.com/testnet/blob/{Blob ID} นั้นเอง ตัวอย่างเช่น

https://walruscan.com/testnet/blob/-80nLPY2ym912Vg54qhABP63izGm1yFLMy7Fg_92-uU

หลังจากนั้นลองเช็คในไฟล์ downloaded-file.txt ดูนะจะได้ข้อมูลแบบนี้

Test content Exclusive for onlyfans!

อธิบายเพิ่มเติมในเเต่ละขั้นตอน

  1. ส่วนนี้จะเป็นการสร้าง connection ซึ่งจริงๆแล้วเราสามารถยิงไปที่ Stroage node ของ Walrus ตรงๆเลยก็ได้ แต่มักจะเกิดปัญหา เราเลยส่งไปที่ Relay แทน แต่ก็เเลกมาด้วยค่า Gas ที่ต้องจ่ายเพิ่มเล็กน้อย

  2. ส่วนนี้ตรงไปตรงมา นั้นคือทำการ Import Private Key ของเรานั้นเอง

  3. เป็นการเตรียมเนื้อหาเพื่อเตรียมส่งไปที่ Walrus

  4. ทำการอัพโหลดข้อมูล ซึ่งเราจะได้ BlogId กลับมาโดยสามารถนำ BlogId ดังกล่าวไปค้นหาผ่าน explorer ได้

  5. ส่วนนี้จะเป็นส่วนของ function ที่เขียนมาเพื่อดึงข้อมูลจาก Storage node จะเห็นว่าที่ต้องวนลูปเพราะบางครั้งเราต้องรอให้ข้อมูลพร้อมก่อน ก่อนที่จะดึงข้อมูลกลับลงมานั้นเอง

ง่ายใช้ไหม ง่ายเเหละ :) นี้แค่ตัวอย่างเบื้องต้นนะ ลองเล่นกันดูก่อนนะ เดียวตอนหน้าเราจะมาดูกันว่า เเล้วถ้าเราจะเเก้ไขไฟล์ละ จะทำได้ยังไงบ้าง