第 13 章 TypeScript SDK 快速上手
Walrus TypeScript SDK 当前以 Mysten SDK 文档为准。本书固定使用:
{
"@mysten/walrus": "1.1.4",
"@mysten/walrus-wasm": "0.2.0",
"@mysten/sui": "2.16.0"
}
最新 SDK 的基本形态是:先创建 SuiGrpcClient,再通过 walrus() 扩展得到 client.walrus。不要再使用旧教程里的 new WalrusClient({ suiRpcUrl, network })。
安装
cd examples/sdk
npm install
示例工程使用 tsx 运行 TypeScript:
npm run write:blob -- ./README.md
npm run read:blob -- <BLOB_ID> ./download.bin
写入需要私钥、SUI gas 和 WAL。设置 WALRUS_PRIVATE_KEY 为 Sui CLI 或钱包导出的 suiprivkey...:
export WALRUS_PRIVATE_KEY="suiprivkey..."
创建客户端
import { walrus } from '@mysten/walrus';
import { SuiGrpcClient } from '@mysten/sui/grpc';
export function createWalrusClient() {
return new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(walrus());
}
walrus() 会根据 Sui client 的 network 使用默认 Testnet 包和对象配置。只有在连接自定义部署、私有网络或官方配置更新尚未进入 SDK 时,才需要手动传 packageConfig。
读取 blob
已知 blob ID 时,最直接的 API 是 readBlob:
const bytes = await client.walrus.readBlob({ blobId });
完整脚本见 examples/sdk/src/read-blob.ts:
import { writeFile } from 'node:fs/promises';
import { createWalrusClient } from './client.js';
const blobId = process.argv[2];
const out = process.argv[3] ?? 'blob.bin';
if (!blobId) {
throw new Error('Usage: npm run read:blob -- <BLOB_ID> [OUT_FILE]');
}
const client = createWalrusClient();
const bytes = await client.walrus.readBlob({ blobId });
await writeFile(out, bytes);
console.log(`wrote ${bytes.length} bytes to ${out}`);
SDK 直连 storage nodes 读取会发起大量请求。面向普通网页下载,aggregator HTTP API 往往更合适;面向需要端到端校验、钱包内操作或自定义恢复逻辑的应用,再使用 SDK 读取。
写入原始 blob
写单个大文件时优先用 writeBlob:
import { readFile } from 'node:fs/promises';
import { createWalrusClient, loadKeypair } from './client.js';
const path = process.argv[2];
if (!path) {
throw new Error('Usage: npm run write:blob -- <FILE>');
}
const client = createWalrusClient();
const signer = loadKeypair();
const blob = await readFile(path);
const result = await client.walrus.writeBlob({
blob,
epochs: Number(process.env.EPOCHS ?? 3),
deletable: process.env.DELETABLE !== 'false',
signer,
});
console.log(JSON.stringify(result, null, 2));
返回值里最常用的是 blobId。保存它用于后续读取:
{
"blobId": "..."
}
写入多个文件
多个小文件用 WalrusFile + writeFiles。当前 SDK 会把传入文件写成一个 quilt,适合页面资源、配置包、小型数据集和批量 JSON。
import { readFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { WalrusFile } from '@mysten/walrus';
import { createWalrusClient, loadKeypair } from './client.js';
const paths = process.argv.slice(2);
if (paths.length === 0) {
throw new Error('Usage: npm run write:files -- <FILE...>');
}
const files = await Promise.all(
paths.map(async (path) =>
WalrusFile.from({
contents: await readFile(path),
identifier: basename(path),
tags: {
'content-type': guessContentType(path),
},
}),
),
);
const client = createWalrusClient();
const results = await client.walrus.writeFiles({
files,
epochs: Number(process.env.EPOCHS ?? 3),
deletable: true,
signer: loadKeypair(),
});
console.log(JSON.stringify(results, null, 2));
读取时可以用 getFiles:
const [file] = await client.walrus.getFiles({ ids: [blobIdOrQuiltId] });
const text = await file.text();
const identifier = await file.getIdentifier();
const tags = await file.getTags();
WalrusFile 的读取体验接近 Fetch API 的 Response:可以调用 bytes()、text()、json(),如果来自 quilt,还能读取 identifier 和 tags。
处理可恢复写入
高层 writeBlob 和 writeFiles 支持 onStep 和 resume。把每一步持久化到数据库或本地文件,进程崩溃后可以从上次完成的位置继续:
const result = await client.walrus.writeBlob({
blob,
epochs: 3,
deletable: true,
signer,
resume: await loadSavedStep(fileId),
onStep: async (step) => {
await saveStep(fileId, step);
},
});
如果你只是在脚本里上传小文件,可以先不用恢复逻辑。后端生产服务、浏览器大文件上传和移动网络场景,必须保存 step。
错误重试
Epoch 切换、storage node 临时不可用、网络超时都可能导致可恢复错误。SDK 暴露 RetryableWalrusClientError:
import { RetryableWalrusClientError } from '@mysten/walrus';
try {
await client.walrus.readBlob({ blobId });
} catch (error) {
if (error instanceof RetryableWalrusClientError) {
client.walrus.reset();
await client.walrus.readBlob({ blobId });
} else {
throw error;
}
}
高层 API 已内置部分重试。你只有在自己写外层任务队列、批量上传或低层 flow 时,才需要显式处理。
自定义请求
Node 默认连接超时可能不适合 Walrus storage node 访问。可以给 walrus() 传 storageNodeClientOptions:
import type { RequestInfo, RequestInit } from 'undici';
import { Agent, fetch } from 'undici';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(
walrus({
storageNodeClientOptions: {
timeout: 60_000,
onError: (error) => console.error(error),
fetch: (url, init) =>
fetch(url as RequestInfo, {
...(init as RequestInit),
dispatcher: new Agent({ connectTimeout: 60_000 }),
}) as unknown as Promise<Response>,
},
}),
);
这属于生产硬化,不是入门必需项。先把读写跑通,再根据日志增加超时、限流和重试。