home / skills / multiversx / mx-ai-skills / mvx_project_architecture

mvx_project_architecture skill

/antigravity/skills/mvx_project_architecture

This skill provides production-grade MultiversX project structure patterns, enabling clean trait-based contract design, modular modules, and safer upgrades.

npx playbooks add skill multiversx/mx-ai-skills --skill mvx_project_architecture

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.3 KB
---
name: mvx_project_architecture
description: Production-grade project structure patterns for MultiversX smart contracts.
---

# MultiversX Project Architecture

## Single Contract Structure

```
my-contract/
├── Cargo.toml
├── src/
│   ├── lib.rs              # #[contract] trait — ONLY trait composition + init/upgrade
│   ├── storage.rs           # All #[storage_mapper] definitions
│   ├── views.rs             # All #[view] endpoints
│   ├── config.rs            # Admin configuration endpoints
│   ├── events.rs            # #[event] definitions
│   ├── validation.rs        # Input validation helpers
│   ├── errors.rs            # Static error constants
│   └── helpers.rs           # Pure business logic functions
├── meta/
├── wasm/
└── tests/
```

## lib.rs Pattern: Trait Composition Only

```rust
#![no_std]
multiversx_sc::imports!();

#[multiversx_sc::contract]
pub trait MyContract:
    storage::StorageModule
    + config::ConfigModule
    + views::ViewsModule
    + events::EventsModule
    + validation::ValidationModule
    + helpers::HelpersModule
{
    #[init]
    fn init(&self) { }

    #[upgrade]
    fn upgrade(&self) { }

    #[endpoint]
    fn main_operation(&self) {
        self.validate_payment(&payment);
        let mut cache = cache::StorageCache::new(self);
        self.process_operation(&mut cache, &payment);
    }
}
```

## errors.rs Pattern

```rust
pub static ERROR_NOT_ACTIVE: &[u8] = b"Contract is not active";
pub static ERROR_INVALID_AMOUNT: &[u8] = b"Invalid amount";
pub static ERROR_UNAUTHORIZED: &[u8] = b"Unauthorized";
pub static ERROR_ZERO_AMOUNT: &[u8] = b"Amount must be greater than zero";
```

## Naming Conventions

| Item | Convention | Example |
|---|---|---|
| Contract crate | kebab-case | `liquidity-pool` |
| Module files | snake_case | `storage.rs` |
| Storage keys | camelCase | `"totalSupply"` |
| Error constants | SCREAMING_SNAKE | `ERROR_INVALID_AMOUNT` |
| Module traits | PascalCase | `StorageModule` |
| Endpoint names | snake_case | `fn deposit_tokens(&self)` |
| View names | camelCase (ABI) | `#[view(getBalance)]` |

## When to Create Common Workspace Crate

Same struct/math/errors/events in 2+ contracts → move to `common/` crate.

Overview

This skill documents production-grade project structure patterns for building MultiversX smart contracts. It prescribes a clear file layout, lib.rs responsibilities, naming conventions, and when to extract shared crates. The goal is consistency, testability, and safe upgrade paths for contracts.

How this skill works

The guide splits contract code into focused modules: a minimal lib.rs that composes trait modules, dedicated files for storage, views, config, events, validation, errors, and helpers, plus separate meta, wasm, and test folders. It enforces lib.rs as trait composition only (init/upgrade/entrypoints call into modules) and centralizes static error strings and naming rules to avoid ambiguity. When multiple contracts share identical logic, it recommends extracting that code into a common workspace crate.

When to use it

  • Starting a new MultiversX smart contract project to enforce maintainable structure
  • Refactoring a monolithic contract into testable, composable modules
  • Preparing contracts for safe upgrades and clear initialization logic
  • Sharing math, struct, error, or event definitions across two or more contracts
  • Standardizing naming and ABI visibility for a team or organization

Best practices

  • Keep lib.rs minimal: only trait composition plus init and upgrade handlers
  • Place persistence mappings in storage.rs and keep keys consistent (camelCase)
  • Define all static error messages in errors.rs using SCREAMING_SNAKE constants
  • Implement pure logic in helpers.rs so functions are easier to unit-test
  • Extract common structs, math, errors, and events to a common workspace crate when reused

Example use cases

  • A single-token liquidity pool contract with storage, views, and admin config separated
  • An upgradeable governance contract where init/upgrade remain thin and modules handle logic
  • A suite of DeFi contracts sharing token math and error types moved into a common crate
  • A wallet or bridge contract with clear validation and event modules for auditability

FAQ

Why keep lib.rs minimal?

Minimizing lib.rs reduces coupling, makes upgrades safer, and forces implementation details into testable modules.

When should I create a common crate?

If the same structs, math, errors, or events appear in two or more contracts, extract them to a shared crate to avoid duplication and ensure consistency.