Building a Data Marketplace
A data marketplace lets sellers publish datasets and buyers pay to acquire them. Walrus provides the building blocks for all three layers of such an application: Walrus itself stores and serves the dataset bytes, a Move package on Sui handles listings, payments, and ownership, and client-side encryption, for example with Seal, keeps paid content confidential. Every blob you store on Walrus has a corresponding Blob object on Sui with the key and store abilities, so datasets become assets that smart contracts can escrow, price, and transfer.
Anyone who knows a blob ID can read the blob through any aggregator. Ownership of the Blob object does not restrict who can read the blob; the object controls management rights such as extending, deleting, and setting attributes. To sell access to the content itself, encrypt the data before you store it and gate decryption onchain, as described in Gate paid content with encryption.
Architecture
A minimal marketplace consists of four parts:
- Seller clients encrypt (optionally) and upload datasets through a publisher, the CLI, or the TypeScript SDK, and receive the resulting
Blobobjects. - A marketplace Move package escrows
Blobobjects in shared listing objects, verifies payment, pays the seller, and transfers the blob to the buyer. - Buyer clients purchase a listing, then read the dataset through an aggregator using the blob ID or the object ID.
- Walrus infrastructure does the heavy lifting: publishers accept uploads, storage nodes hold the encoded slivers, and aggregators serve reads.
Store the dataset
Sellers first store the dataset on Walrus. The following curl command stores a file through a publisher for 10 epochs as a permanent blob and sends the created Blob object to the seller's address:
$ curl -X PUT \
"$PUBLISHER/v1/blobs?epochs=10&permanent=true&send_object_to=$SELLER_ADDRESS" \
--upload-file "dataset.bin"
Three query parameters matter for marketplace listings:
epochssets the storage duration. The publisher stores the blob for 1 epoch if you omit it.permanent=truerules out deletion before expiry. By default the publisher lets the owner delete a new blob, and buyers expect that a seller cannot delete data after a sale.send_object_tosends the createdBlobobject to the given Sui address, so the seller's wallet holds the object to list.
Set $PUBLISHER to an endpoint from the Network Reference. Walrus has no public unauthenticated publisher on Mainnet, and most public endpoints limit requests to 10 MiB; for production stores or larger datasets, run your own publisher or use the CLI or TypeScript SDK. The response carries the blobId, the handle buyers later use to read the data, and, for newly created blobs, the Sui object ID of the Blob object. See Understanding the response.
Escrow and sell the blob in Move
A marketplace contract escrows the Blob object so that a buyer can purchase it. The primitive that makes this possible is wrapping: because Blob has the store ability, a Move package can take a blob by value and hold it inside its own object. The Walrus repository ships that primitive as a runnable example:
docs/examples/move/walrus_dep/sources/wrapped_blob.move. You probably need to run `pnpm prebuild` and restart the site.A marketplace extends this pattern in three ways:
- Add listing terms to the wrapper. Alongside the escrowed blob, store the price and the seller's address in the same struct.
- Share the wrapper instead of holding it.
transfer::share_objectmakes the listing reachable by any buyer, rather than only its owner. - Add a purchase function. Unpack the listing, verify the payment coin, transfer the payment to the seller, and transfer the
Blobobject to the buyer withtransfer::public_transfer.
After the purchase, the buyer owns the Blob object and can extend its lifetime, set attributes, resell it, or wrap it again. While a wrapper holds a blob in escrow, only the wrapping module's functions can reach it, so add explicit functions for any management the seller still needs.
Your package needs the Walrus dependency in its Move.toml; see Add the Walrus dependency for the exact declaration and build commands.
Gate paid content with encryption
Because anyone can read any blob you store, a marketplace that sells the content itself, rather than provenance or management rights, must encrypt datasets before storing them. Seal provides threshold encryption with onchain access control:
- The seller encrypts the dataset with Seal under an access policy: a Move function named
seal_approvein your package decides who can decrypt. Only the ciphertext goes to Walrus. - The marketplace contract records each purchase onchain, for example by adding the buyer to an allowlist that the policy checks.
- The buyer downloads the ciphertext from an aggregator and requests key shares from the Seal key servers, which check the onchain policy before answering. The buyer then decrypts locally.
For a runnable end-to-end example that encrypts with Seal and stores the ciphertext on Walrus, see the Seal example code in the Walrus repository.
Attach product metadata
Marketplace listings need titles, descriptions, and content types. You can attach this metadata directly to the Blob object:
-
With the CLI, set key-value attributes on a blob you own.
$ walrus set-blob-attribute <BLOB_OBJ_ID> --attr "content-type" "text/csv" -
From Move, the
walrus::blobmodule providesinsert_or_update_metadata_pairand related functions to manage metadata on a blob your contract holds.
When buyers read a blob by object ID, the aggregator returns recognized attribute keys, such as content-type and content-disposition, in the corresponding HTTP response headers. For many small preview files or product cards, Quilt batches them into a single stored unit and cuts per-blob overhead.
Deliver the data to buyers
Buyers read blobs with a GET request to any aggregator:
# Read by blob ID
$ curl "$AGGREGATOR/v1/blobs/<BLOB_ID>" -o dataset.bin
# Read by the Blob object ID, which also returns attribute headers
$ curl "$AGGREGATOR/v1/blobs/by-object-id/<BLOB_OBJECT_ID>" -o dataset.bin
For encrypted listings, the buyer decrypts locally after Seal releases the key shares. If a read right after upload returns a 404 through a CDN-fronted aggregator, retry with backoff; see Reading Blobs Right After Upload.
Keep listings available
Walrus stores each blob for a fixed number of epochs, so a marketplace must plan for expiry:
- The blob owner extends a blob with
walrus extend --blob-obj-id <ID>, and a contract extends a blob it holds throughwalrus::system::extend_blobwith a WAL payment, for example funded from sale proceeds. - A shared blob wraps a blob in a shared object that anyone can fund and extend, which suits listings the whole marketplace wants to keep alive. Create one with
walrus share --blob-obj-id <SUI_OBJ_ID>. - Storage resources themselves transfer between users, so a marketplace can also acquire and trade storage capacity; see Storage Costs.