Waldrop SDK · Recipe

Forecast upload cost before you commit

Read live Walrus pricing and compute total cost in FROST/WAL for a planned upload — no wallet needed, no signature, just a public RPC read.

Quickest possible recipe

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

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

const r = await client.cost.estimate({
  bytesPerBlob: 10 * 1024 * 1024,    // 10 MiB
  epochs: 26,                         // 26 days on testnet
});

console.log(`${r.totalWal.toFixed(6)} WAL`);
// → "0.001658 WAL"

console.log(`storage ${r.storageFrost} · write ${r.writeFrost} · units ${r.totalUnits}`);
// → "storage 1539200 · write 118400 · units 50"

That's the whole thing. No signer, no subscription, no wallet — just a new WaldropClient() and a read.

Show the user before they upload

Wire it into a wizard step so the cost surfaces before the wallet prompt appears:

function CostPreview({ file, epochs }: { file: File; epochs: number }) {
  const [estimate, setEstimate] = useState<EstimateResult | null>(null);

  useEffect(() => {
    let cancelled = false;
    client.cost.estimate({
      bytesPerBlob: file.size,
      epochs,
    }).then((r) => {
      if (!cancelled) setEstimate(r);
    });
    return () => { cancelled = true; };
  }, [file.size, epochs]);

  if (!estimate) return <Spinner />;
  if (!estimate.isAvailable) return <p>Pricing unavailabletry again.</p>;

  return (
    <p>
      Estimated cost: <b>{estimate.totalWal.toFixed(6)} WAL</b>
      {" "}({estimate.totalUnits} × 1 MiB units)
    </p>
  );
}

Memoize pricing across many estimates

client.cost.estimate re-fetches Walrus pricing every call by default. If you're computing N estimates back-to-back (e.g. a per-file table), fetch once and pass it through:

const pricing = await client.cost.getPricing();

const estimates = files.map((f) =>
  client.cost.estimate({
    bytesPerBlob: f.size,
    epochs: 26,
    pricing,                       // ← skips the live fetch
  }),
);

const all = await Promise.all(estimates);

Multiple blobs at once

const r = await client.cost.estimate({
  bytesPerBlob: 1 * 1024 * 1024,
  numBlobs: 100,                   // 100 separate 1 MiB blobs
  epochs: 26,
});

console.log(r.totalUnits);         // 500 (5× redundancy × 100 blobs)
console.log(r.totalWal);           // cost for the whole batch

For a tar bundle, the bundle is a single blob — pass bytesPerBlob: tar.size and numBlobs: 1, not the file count.

What's in the result

{
  totalFrost: 1657600,        // total in FROST (1 WAL = 10⁹ FROST)
  storageFrost: 1539200,      // units × storage-price × epochs
  writeFrost: 118400,         // units × write-price (one-time)
  totalUnits: 50,             // encoded 1 MiB storage units
  totalWal: 0.001658,         // totalFrost / 1e9
  isAvailable: true,
  pricing: {
    currentEpoch: 396,
    epochDurationMs: 0,                 // testnet: 0 (lives on Staking obj)
    epochDays: 0,
    storagePricePerUnitFrost: 1184,     // FROST/unit/epoch
    writePricePerUnitFrost: 2368,       // FROST/unit one-time
    isAvailable: true,
  },
}
Why 5× — the encoded-bytes multiplier

Walrus encodes data with RaptorQ + erasure coding across storage shards. Your 1 MiB pays for ~5 MiB of encoded storage. The factor depends on n_shards, but 5× is a safe upper bound used by the dapp + SDK. The publisher does the authoritative calculation at PUT time, so this estimate is approximate-but-bounded.

Days unavailable on testnet

epochDays returns 0 on testnet because epoch_duration_ms lives on the Walrus StakingInner object, not the System object. Default to "1 day" for testnet display; query the staking object directly if you need mainnet's 14-day epochs.

Edit this page on GitHub ↗
Waldrop · 2026cryptokarigar