# Walrus Move Integration

> Referencing and wrapping Walrus blobs in Sui Move smart contracts. Use when the user needs to wrap a Walrus Blob object in a custom Move struct, add Walrus as a Move dependency, build a contract that depends on the Walrus package, or integrate on-chain logic with Walrus blob storage. Also use when the user asks about wrapped_blob.move, the Walrus Move package, or how to reference blobs from Move code.

- Skill: `mystenlabs/walrus-move-integration` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add mystenlabs/walrus-move-integration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mystenlabs/walrus-move-integration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: MystenLabs (https://skillmd.com/u/mystenlabs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mystenlabs/walrus-move-integration

---


# Walrus Move Integration

> **Source constraint:** All information in this skill is sourced from the
> [Walrus Move example](https://github.com/MystenLabs/walrus/tree/main/docs/examples/move/walrus_dep)
> and the [Walrus documentation](https://docs.wal.app). When extending this skill,
> only pull from these sources.

Walrus blobs are represented on-chain as `walrus::blob::Blob` Sui objects. You can reference, wrap, or compose with these objects in your own Move contracts. This enables patterns like NFTs backed by Walrus-stored media, access-controlled document registries, or any on-chain logic that needs to track off-chain blob data.

All patterns in this skill are derived from:
- https://github.com/MystenLabs/walrus/tree/main/docs/examples/move/walrus_dep
- https://github.com/MystenLabs/walrus/tree/main/contracts/walrus

If unsure about the Walrus Move API, check the source contracts before answering.

---

## Related skills

| Topic | Skill | Load when |
|-------|-------|-----------|
| Move fundamentals | `sui-move` | Writing Move code on Sui (abilities, TxContext, init) |
| Object model | `object-model` | Ownership types, wrapping, dynamic fields |
| Project setup | `sui-move-project` | Move.toml configuration, dependencies |
| Blob lifecycle | `walrus-blob-lifecycle` | Extending, deleting, sharing blobs |
| Encryption | `walrus-data-security` | Encrypting blob content before storage |

---

## Skill Content

### Key concepts

- **`walrus::blob::Blob`.** The core Move type representing a Walrus blob on-chain. It has the `key` ability (it is a Sui object) but not `store`, which means it cannot be placed inside other objects using direct field embedding in all contexts. Check the latest contract source for current abilities.

- **Wrapping pattern.** Create a custom struct with `key` ability that contains a `Blob` field. This "wraps" the blob, giving your contract control over access and transfer. The wrapper struct needs its own `UID`.

- **Wrapping blobs in shared objects.** A `Blob` can be wrapped into any Sui shared object you define, enabling multiple parties to fund and extend it. The Walrus contract provides `shared_blob::SharedBlob` as a reference implementation ([source](https://github.com/MystenLabs/walrus/tree/main/contracts/walrus/sources/system/shared_blob.move)), but you can create your own wrapper with custom access control, metadata, or business logic.

### Move.toml setup

Add the Walrus package as a dependency in your `Move.toml`. The correct `subdir` and `rev` depend on whether you target **testnet** or **mainnet**:

#### Testnet

```toml
[package]
name = "my_walrus_app"
edition = "2024"

[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "testnet-v1.35.0" }
Walrus = { git = "https://github.com/MystenLabs/walrus.git", rev = "testnet", subdir = "testnet-contracts/walrus" }

[addresses]
sui = "0x2"
my_walrus_app = "0x0"
```

#### Mainnet

```toml
[package]
name = "my_walrus_app"
edition = "2024"

[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.35.0" }
Walrus = { git = "https://github.com/MystenLabs/walrus.git", rev = "main", subdir = "contracts/walrus" }

[addresses]
sui = "0x2"
my_walrus_app = "0x0"
```

**Critical notes:**
- Use `edition = "2024"`, not `"2024.beta"`. The beta edition is outdated.
- For testnet, use `subdir = "testnet-contracts/walrus"` (not `contracts/walrus`). The testnet contracts are published from a different directory.
- If you also need the WAL token type, add: `Wal = { git = "https://github.com/MystenLabs/walrus.git", rev = "testnet", subdir = "testnet-contracts/wal" }`
- Pin `rev` to a specific commit hash for reproducible builds rather than using branch names.
- The Sui framework `rev` must be compatible with the Walrus package's Sui dependency. Check the Walrus repo's `Move.toml` for the expected version.

### Example: WrappedBlob

The canonical example wraps a `Blob` inside a custom object:

```move
module my_app::wrapped_blob {
    use walrus::blob::Blob;

    public struct WrappedBlob has key {
        id: UID,
        blob: Blob,
    }

    public fun wrap(blob: Blob, ctx: &mut TxContext): WrappedBlob {
        WrappedBlob { id: object::new(ctx), blob }
    }
}
```

This gives your module ownership of the blob. You can add access control, metadata, or business logic around it.

### Building and testing

```sh
# Build the project
sui move build

# Run tests
sui move test
```

The Walrus dependency is fetched from Git during build. Ensure network access is available.

### Rules

1. **Pin the Walrus dependency revision.** Use a specific commit hash or tag in `Move.toml` rather than `main` for reproducible builds.
2. **Match the Sui framework version.** The Sui framework `rev` in your `Move.toml` must be compatible with the Walrus package's Sui dependency. Check the Walrus repo's `Move.toml` for the expected Sui version.
3. **Wrapped blobs need their own UID.** A wrapper struct must have `key` ability and its own `id: UID` field, not just the blob's UID.
4. **Lifecycle operations go through the Walrus system contract.** To extend, delete, or certify blobs, interact with the Walrus system contract, not the `Blob` struct directly.
5. **Blob content is off-chain.** The `Blob` Move object contains metadata (blob ID, size, epoch info). The actual data lives on Walrus storage nodes. To read content, use the CLI, SDK, or HTTP API.

### Debugging `VMVerificationOrDeserializationError`

This error occurs when publishing or calling a contract that depends on Walrus. Common causes:

1. **Wrong `subdir` for the network.** Testnet contracts are in `testnet-contracts/walrus`, not `contracts/walrus`. Using the wrong subdir means you build against a different package than what is deployed on-chain.
2. **Wrong `rev`.** The commit must match a revision where the contracts are compatible with the deployed on-chain package. For testnet, use the `testnet` branch or a pinned commit from that branch.
3. **Stale `published-at` or `original-id` in Move.lock.** Delete the `Move.lock` file and rebuild.
4. **Package version mismatch.** Walrus uses versioned shared objects. If you call a function using an old package ID, you get `EWrongVersion` (abort code 1) from `system::inner_mut`. The Walrus SDK/CLI auto-refreshes package IDs, but custom implementations must handle this.

To find the current package IDs:
- Check `walrus info` output or the [available networks page](https://docs.wal.app/docs/system-overview/available-networks)
- The client auto-infers package IDs from `system_object` and `staking_object`

### SharedBlob API (reference implementation)

The `walrus::shared_blob` module is one example of wrapping a `Blob` in a shared object. Developers can freely wrap a `Blob` into their own shared object with custom logic instead.

```move
// Create a shared blob from an owned Blob (must be permanent)
walrus::shared_blob::new(blob: Blob, ctx: &mut TxContext): SharedBlob

// Extend a shared blob's lifetime (anyone can call)
walrus::shared_blob::extend(shared_blob: &mut SharedBlob, ...)
```

The built-in `SharedBlob` is a shared Sui object. Anyone can fund and extend it. It requires a permanent blob.

### Common mistakes

- **Trying to read blob content from Move.** Move contracts only see blob metadata (ID, size, epoch, storage info). Actual blob data is stored off-chain on Walrus storage nodes.
- **Using `main` as the dependency rev in production.** The `main` branch can change at any time. Pin to a specific commit for stable builds.
- **Using `contracts/walrus` subdir for testnet.** Testnet uses `testnet-contracts/walrus`. This is the number one cause of `VMVerificationOrDeserializationError` when publishing to testnet.
- **Using `edition = "2024.beta"`.** Use `edition = "2024"`. The beta edition is outdated and can cause build errors.
- **Mismatched Sui framework versions.** If your Sui framework version does not match what Walrus expects, the build fails with confusing dependency resolution errors. Check the Walrus repo's `Move.toml`.
- **Forgetting to add the `Walrus` dependency address.** The `walrus` address is provided by the Walrus package dependency. You do not need to add it to `[addresses]`.
- **Calling Walrus system functions with a stale package ID.** After a Walrus upgrade, the old package ID no longer works. The SDK/CLI handle this automatically, but direct Move contract calls must use the current package. Query the system object to find the latest package ID.

