Waldrop SDK · Getting Started

Your first upload

Five minutes from npm install to a verifiable blob on testnet Walrus. Walks through every credential you need, plus a hands-on recovery from the most common first-time failure (out of SUI for gas).

What you'll need

01
A testnet Sui keypair

Funded with at least 0.01 SUI. Generate via Sui Wallet or
sui client new-address ed25519.

02
A Waldrop subscription id

Subscribe once via the dapp at
waldrop.xyz. The Subscription object id will appear in your Sui owned-objects list.

If you already did bun add @waldrop/sdk @mysten/sui, you have the deps. Otherwise:

npm install @waldrop/sdk @mysten/sui

Step 1 — Fund the wallet

curl -X POST https://faucet.testnet.sui.io/v2/gas \
  -H "Content-Type: application/json" \
  -d '{"FixedAmountRequest":{"recipient":"0xYOUR_ADDRESS"}}'

Or click the faucet button inside Sui Wallet → testnet. One drop is 1 SUI, which is enough for ~125 register-blob transactions.

Step 2 — Find your Subscription id

The fastest way: visit https://waldrop.xyz in your wallet, subscribe to a plan (Free is fine — no payment). The Subscription object's id appears in the dapp's account screen, or in your Sui Wallet owned objects.

Or query directly:

import { WaldropClient } from "@waldrop/sdk";

const client = new WaldropClient({ network: "testnet" });
const sub = await client.subscription.get({ owner: "0xYOUR_ADDRESS" });

console.log(sub?.subscriptionId);

null here means you haven't subscribed yet — go through the dapp once.

Step 3 — Write the script

Save this as upload.ts:

import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
import { SuiGrpcClient } from "@mysten/sui/grpc";
import type { Transaction } from "@mysten/sui/transactions";
import {
  WaldropClient,
  InsufficientGasError,
  RegistrationError,
  type TransactionSigner,
} from "@waldrop/sdk";

const { secretKey } = decodeSuiPrivateKey(process.env.WALDROP_PRIVATE_KEY!);
const keypair = Ed25519Keypair.fromSecretKey(secretKey);
const sender = keypair.toSuiAddress();

const suiClient = new SuiGrpcClient({
  network: "testnet",
  baseUrl: "https://fullnode.testnet.sui.io:443",
});

const signer: TransactionSigner = {
  async signAndExecuteTransaction({ transaction }) {
    const r = await (suiClient as any).signAndExecuteTransaction({
      transaction: transaction as Transaction,
      signer: keypair,
      include: { effects: true },
    });
    const digest = r?.digest ?? r?.effects?.transactionDigest ?? "";
    if (digest) await (suiClient as any).waitForTransaction?.({ digest });
    return { digest, effects: r?.effects };
  },
};

const client = new WaldropClient({ network: "testnet", suiClient });

try {
  const result = await client.blob.upload({
    data: new TextEncoder().encode(`hello waldrop · ${new Date().toISOString()}`),
    fileName: "hello.txt",
    contentType: "text/plain",
    epochs: 2,
    senderAddress: sender,
    subscriptionId: process.env.WALDROP_SUBSCRIPTION_ID!,
    signer,
    onProgress: (e) => console.log(`  [${e.stage}] ${e.percent}%`),
  });

  console.log(`✓ blob landed: ${result.blobId}`);
  console.log(`  tx: https://testnet.suivision.xyz/txblock/${result.transactionDigest}`);
} catch (err) {
  if (err instanceof InsufficientGasError) {
    console.error(`✗ wallet needs ${err.requiredMist} MIST — top up via faucet.`);
  } else if (err instanceof RegistrationError) {
    console.error("✗ tx failed; bytes are safe on Walrus.", err.message);
    console.error("  blobId:", err.checkpoint.blobId);
  } else {
    throw err;
  }
}

Step 4 — Run it

export WALDROP_PRIVATE_KEY=suiprivkey1...     # from your wallet export
export WALDROP_SUBSCRIPTION_ID=0x...          # from step 2

bun upload.ts

Expected output:

  [uploading] 20%
  [registering] 75%
  [done] 100%
blob landed: Zb4z_LLozYWPNhTFHO15Zq15AT0FDuwAt4tITl-rh8w
  tx: https://testnet.suivision.xyz/txblock/...

Click the link — you'll see your register_blob Move call in Suivision.

Step 5 — Read it back

Add this at the bottom of upload.ts:

const { bytes, contentType } = await client.blob.fetch({
  blobId: result.blobId,
});

console.log("  type:", contentType);
console.log("  text:", new TextDecoder().decode(bytes));

Re-run — you'll see your original message printed back. Round trip in 6 seconds.

That's it

You now have a working Waldrop pipeline. The same client.blob.upload call handles encryption, sharing, multi-file bundles, and progress callbacks. See the Recipes section for each variation.

If it failed

The single most common first-run failure is insufficient gas:

wallet needs 7694984 MISTtop up via faucet.

This is actually a successful publisher PUT — your bytes already landed on Walrus. Top up SUI and call client.blob.registerOnly({ checkpoint, … }) with the checkpoint from the error. See the Resume guide for the recovery pattern.

Edit this page on GitHub ↗
Waldrop · 2026cryptokarigar