Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 114 additions & 2 deletions docs/content/examples/data-marketplace.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
---
title: Building a Data Marketplace
draft: true
description: >-

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have an example app to support this architecture and design? if not, we should have one before we publish this doc. Conceptually it is fine, but it would be better to have a solid code example to point to that supports this document's claims

Build a data marketplace on Walrus by storing datasets as blobs, escrowing and selling the
resulting Sui Blob objects from a Move package, and gating paid content with encryption.
keywords:
- data marketplace
- blob object
- move
- sell data
- escrow
- seal
goal:
description: Reader can understand how to build a data marketplace backed by Walrus storage
requires:
Expand All @@ -21,5 +30,108 @@ goal:
questions:
- How do I build a data marketplace on Walrus?
- How do I build a data marketplace?
- How do I get started with building a data marketplace?
- How do I sell data stored on Walrus?
answer: >-
A data marketplace on Walrus uses three layers: Walrus stores the dataset bytes as blobs, a Move
package on Sui escrows and sells the corresponding Blob objects, and client-side encryption with
Seal gates access to paid content. Sellers store data through a publisher and list the resulting
Blob object at a price; buyers pay through the Move contract and read the data through an
aggregator.
---

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](https://seal-docs.wal.app/), 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.

:::caution Anyone can read blob data

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](#gate-paid-content-with-encryption).

:::

## Architecture

A minimal marketplace consists of four parts:

1. **Seller clients** encrypt (optionally) and upload datasets through a publisher, the CLI, or the TypeScript SDK, and receive the resulting `Blob` objects.
2. **A marketplace Move package** escrows `Blob` objects in shared listing objects, verifies payment, pays the seller, and transfers the blob to the buyer.
3. **Buyer clients** purchase a listing, then read the dataset through an aggregator using the blob ID or the object ID.
4. **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:

```sh
$ 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:

- `epochs` sets the storage duration. The publisher stores the blob for 1 epoch if you omit it.
- `permanent=true` rules 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_to` sends the created `Blob` object to the given Sui address, so the seller's wallet holds the object to list.

Set `$PUBLISHER` to an endpoint from the [Network Reference](/docs/network-reference#aggregators-and-publishers). 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](/docs/walrus-client/storing-blobs) or [TypeScript SDK](/docs/typescript-sdk/sdks). 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](/docs/http-api/storing-blobs#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:

<ImportContent source="docs/examples/move/walrus_dep/sources/wrapped_blob.move" mode="code" org="MystenLabs" repo="walrus" />

A marketplace extends this pattern in three ways:

1. **Add listing terms to the wrapper.** Alongside the escrowed blob, store the price and the seller's address in the same struct.
2. **Share the wrapper instead of holding it.** `transfer::share_object` makes the listing reachable by any buyer, rather than only its owner.
3. **Add a purchase function.** Unpack the listing, verify the payment coin, transfer the payment to the seller, and transfer the `Blob` object to the buyer with `transfer::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](/docs/examples/move#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](https://seal-docs.wal.app/) provides threshold encryption with onchain access control:

1. The seller encrypts the dataset with Seal under an access policy: a Move function named `seal_approve` in your package decides who can decrypt. Only the ciphertext goes to Walrus.
2. The marketplace contract records each purchase onchain, for example by adding the buyer to an allowlist that the policy checks.
3. 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](https://github.com/MystenLabs/walrus/tree/main/docs/examples/seal) 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.

```sh
$ walrus set-blob-attribute <BLOB_OBJ_ID> --attr "content-type" "text/csv"
```

- From Move, the `walrus::blob` module provides `insert_or_update_metadata_pair` and 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](/docs/system-overview/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:

```sh
# 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](/docs/troubleshooting/reading-blobs-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 through `walrus::system::extend_blob` with a WAL payment, for example funded from sale proceeds.
- A [shared blob](/docs/walrus-client/managing-blobs#shared-blobs) 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](/docs/system-overview/storage-costs).
1 change: 1 addition & 0 deletions docs/site/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const sidebars = {
link: { type: "doc", id: "examples/index" },
items: [
"examples/checkpoint-data",
"examples/data-marketplace",
"examples/javascript",
"examples/move",
"examples/python",
Expand Down
Loading