| name | walrus-move-integration |
| description | 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.
|
Walrus Move Integration
Source constraint: All information in this skill is sourced from the
Walrus Move example
and the Walrus documentation. 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:
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), 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
[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
[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:
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
sui move build
sui move test
The Walrus dependency is fetched from Git during build. Ensure network access is available.
Rules
- Pin the Walrus dependency revision. Use a specific commit hash or tag in
Move.toml rather than main for reproducible builds.
- 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.
- 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.
- 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.
- 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:
- 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.
- 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.
- Stale
published-at or original-id in Move.lock. Delete the Move.lock file and rebuild.
- 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
- 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.
// 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.