EVM × Sui Walrus GitHub
Showcase 05 · implemented

Token lists resolved via one eth_call, no IPNS lottery.

An 80-line TypeScript client that resolves an ENS name (e.g. tokens.uniswap.eth) to a Walrus blob via the WalrusResolver contract from showcase 03, then fetches the manifest body from any Walrus aggregator by its content-addressed blob id. The pointer lookup is deterministic; the bytes are content-addressed; opt-in client-side hash verification is one Walrus SDK call away.

Source: 05-verifiable-manifest/ Depends on: showcase 03 · WalrusResolver Stack: TypeScript · viem · Walrus aggregator
01 · the pain

Public gateways flake exactly when token lists matter most.

gateways
ipfs-pain.md · §2
"ipfs.io 85–90% of the time, it resolves … sometimes takes 5 min" — and Uniswap's interface release notes broke because public gateways flake. Token lists and dApp manifests are exactly the small, high-read, cache-friendly artifact that public-gateway flakiness ruins.
— 2026 builder-sentiment survey
02 · the shape

Deterministic pointer + content-addressed body + optional verifier.

The trust boundary is the aggregator — same model as fetching by CID from an IPFS gateway. The leverage over IPFS is that the pointer lookup is now an EVM read, not an IPNS lottery.

The client validates that the on-chain contentType shortcode starts with app/json before parsing, so non-JSON pointers fail loud instead of silently corrupting the schema.

For trustless retrieval, re-encode the returned bytes with @mysten/walrus and assert the hash matches the on-chain blobId. The 80-line client doesn't do this by default — it's the showcase's job to demonstrate the pointer swap; verification is one composition away.

// the entire public surface
export interface ResolvedManifest<T> {
  manifest: T;
  blobId: `0x${string}`;
  contentType: string;
}

export async function resolveManifest<T>(
  ensName: string,
  opts: ResolveOpts,
): Promise<ResolvedManifest<T>>;

// + a typed wrapper for the Uniswap TokenList shape
export const resolveTokenList = (ens, opts) =>
  resolveManifest<UniswapTokenList>(ens, opts);
pointer.call
aggregator GET
JSON.parse
03 · try it locally

Resolve tokens.uniswap.eth (or your own ENS-pointed name).

01

Have a WalrusResolver pointer in place

This client depends on the WalrusResolver from showcase 03. Either:

  • Use the one you deployed in showcase 03 + a name whose subdomain you set with setWalrusBlob (content-type app/json), or
  • Point at a publicly-deployed WalrusResolver if your team has one, or
  • Deploy a fresh one and call setWalrusBlob with the blobId of a JSON manifest you've already uploaded to Walrus.
02

Install + configure

cd showcases/05-verifiable-manifest
pnpm install
EnvRequired?Purpose
EVM_RPC_URLyesMainnet (or wherever your WalrusResolver lives + the ENS registry it points at).
RESOLVER_ADDRESSyesDeployed WalrusResolver address.
ENS_NAMEdefault tokens.uniswap.ethThe ENS name whose subdomain holds the manifest pointer.
AGGREGATORdefault testnetWalrus aggregator URL (any will do — same blobId, same bytes).
03

Run the example

pnpm tsx example-usage.ts
# → Resolved tokens.uniswap.eth → blobId=0x... (app/json)
# → Token list "Uniswap Default" contains 423 tokens.
# →   - USDC      0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
# →   - DAI       0x6b175474e89094c44da98b954eedeac495271d0f
# →   - ...

Or use it from your own code:

import { resolveTokenList } from "walrus-verifiable-manifest";

const { manifest, blobId, contentType } = await resolveTokenList("tokens.uniswap.eth", {
  rpcUrl: EVM_RPC_URL,
  resolverAddress: RESOLVER_ADDRESS,
  aggregator: "https://aggregator.walrus-testnet.walrus.space",
});
04

(Opt-in) trustless retrieval — recompute the hash

If your threat model requires the bytes to be trustless against the aggregator, run the returned manifest through @mysten/walrus's Reed-Solomon encoder and assert the recomputed blob id matches the on-chain one. The 80-line client deliberately keeps this opt-in to stay minimal.

04 · verify

What "good" looks like.

05 · what this is NOT

Limits to set expectations.

  • Not a hash verifier. Trust in the aggregator's bytes equals trust in an IPFS gateway. The leverage over IPFS is the pointer, not the bytes — verification is composable on top via @mysten/walrus.
  • Not a token-list authoring tool. You still need to author the JSON and upload it to Walrus yourself (any of the showcase 01 / 03 / 04 upload paths work).
  • Not multi-chain magic. The client takes a single EVM_RPC_URL — it doesn't proxy across chains. Run one resolver per chain you care about, or wrap with a thin multi-chain dispatcher.
  • Not extensible past app/json. The contentType shortcode is checked at the start. Other content types are out of grammar for this client — fork it if you want PNG-pointer or markdown-pointer manifests.
06 · source files

Where to look in the repo.