name: multiversx-project-architecture description: MultiversX 智能合约的生产级项目结构模式。用于开始新的合约项目、重构现有项目或构建具有共享代码的多合约系统。
MultiversX 项目架构
MultiversX 合约的生产测试文件夹结构和模块组合模式。
单合约结构
对于业务逻辑约 1000 行的合约:
my-contract/
├── Cargo.toml
├── src/
│ ├── lib.rs # #[contract] trait — 仅包含特质组合 + 初始化/升级
│ ├── storage.rs # 所有 #[storage_mapper] 定义
│ ├── views.rs # 所有 #[view] 端点
│ ├── config.rs # 管理员配置端点
│ ├── events.rs # #[event] 定义
│ ├── validation.rs # 输入验证辅助函数
│ ├── errors.rs # 静态错误常量
│ └── helpers.rs # 纯业务逻辑函数
├── meta/
│ ├── Cargo.toml
│ └── src/main.rs
├── wasm/
│ ├── Cargo.toml
│ └── src/lib.rs
└── tests/
└── integration_test.rs
多合约工作空间结构
对于具有多个交互合约的协议:
my-protocol/
├── Cargo.toml # [workspace] 成员
├── common/ # 所有合约共享的crate
│ ├── Cargo.toml
│ ├── constants/
│ │ ├── Cargo.toml
│ │ └── src/lib.rs # 协议范围内的常量
│ ├── errors/
│ │ ├── Cargo.toml
│ │ └── src/lib.rs # 共享错误常量
│ ├── structs/
│ │ ├── Cargo.toml
│ │ └── src/lib.rs # 共享数据类型
│ ├── math/
│ │ ├── Cargo.toml
│ │ └── src/math.rs # 共享数学模块特质
│ └── events/
│ ├── Cargo.toml
│ └── src/lib.rs # 共享事件定义
├── contract-a/ # 第一个合约
│ ├── Cargo.toml
│ ├── src/
│ │ ├── lib.rs
│ │ ├── storage/
│ │ │ └── mod.rs # 本地 + 代理存储
│ │ ├── cache/
│ │ │ └── mod.rs # 基于Drop的缓存
│ │ ├── views.rs
│ │ ├── config.rs
│ │ ├── events.rs
│ │ ├── validation.rs
│ │ └── helpers/
│ │ └── mod.rs
│ ├── meta/
│ ├── wasm/
│ └── tests/
├── contract-b/
│ └── ... # 相同结构
└── proxy-definitions/ # 可选:共享代理特质
├── Cargo.toml
└── src/lib.rs
lib.rs 模式:仅特质组合
主合约文件应仅组合模块 — 无业务逻辑:
#![no_std]
multiversx_sc::imports!();
pub mod cache;
pub mod config;
pub mod events;
pub mod helpers;
pub mod storage;
pub mod validation;
pub mod views;
#[multiversx_sc::contract]
pub trait MyContract:
storage::StorageModule
+ config::ConfigModule
+ views::ViewsModule
+ events::EventsModule
+ validation::ValidationModule
+ helpers::HelpersModule
+ common_math::SharedMathModule
{
#[init]
fn init(&self, /* 参数 */) {
// 仅初始化逻辑
}
#[upgrade]
fn upgrade(&self) {
// 仅升级迁移逻辑
}
#[endpoint]
fn main_operation(&self) {
// 委托给 helpers/validation
self.validate_payment(&payment);
let mut cache = cache::StorageCache::new(self);
self.process_operation(&mut cache, &payment);
// cache 销毁并提交
}
}
errors.rs 模式
使用 pub static 字节字符串引用以实现高效的 gas 消耗错误消息:
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_NOT_SUPPORTED: &[u8] = b"Not supported";
pub static ERROR_INSUFFICIENT_BALANCE: &[u8] = b"Insufficient balance";
pub static ERROR_ZERO_AMOUNT: &[u8] = b"Amount must be greater than zero";
用法:
require!(amount > 0u64, ERROR_ZERO_AMOUNT);
这比内联字符串字面量更 gas 高效,因为编译器会去重静态引用。
events.rs 模式
将事件定义为单独的模块特质:
#[multiversx_sc::module]
pub trait EventsModule {
#[event("deposit")]
fn deposit_event(
&self,
#[indexed] caller: &ManagedAddress,
#[indexed] token: &TokenId,
amount: &BigUint,
);
#[event("withdraw")]
fn withdraw_event(
&self,
#[indexed] caller: &ManagedAddress,
#[indexed] token: &TokenId,
amount: &BigUint,
);
}
validation.rs 模式
集中所有输入验证:
#[multiversx_sc::module]
pub trait ValidationModule: crate::storage::StorageModule {
fn validate_payment(&self, payment: &Payment<Self::Api>) {
self.require_token_supported(&payment.token_identifier);
self.require_amount_positive(payment.amount.as_big_uint());
}
fn require_token_supported(&self, token: &TokenId<Self::Api>) {
require!(self.supported_tokens().contains(token), ERROR_NOT_SUPPORTED);
}
fn require_amount_positive(&self, amount: &BigUint) {
require!(amount > &BigUint::zero(), ERROR_ZERO_AMOUNT);
}
}
何时创建公共工作空间 crate
| 信号 | 操作 |
|---|---|
| 相同结构体在 2+ 个合约中使用 | 移动到 common/structs/ |
| 相同数学函数在 2+ 个合约中使用 | 移动到 common/math/ |
| 相同错误消息跨合约 | 移动到 common/errors/ |
| 相同事件定义 | 移动到 common/events/ |
| 协议常量(精度、限制) | 移动到 common/constants/ |
工作空间 Cargo.toml
[workspace]
members = [
"contract-a",
"contract-a/meta",
"contract-b",
"contract-b/meta",
"common/structs",
"common/math",
"common/errors",
"common/constants",
"common/events",
]
公共 crate Cargo.toml:
[package]
name = "common-structs"
version = "0.0.0"
edition = "2024"
[dependencies.multiversx-sc]
version = "0.64.0"
SDK 标准模块(multiversx-sc-modules)
SDK 提供的可重用模块。在 Cargo.toml 中导入并在合约特质中继承。
[dependencies.multiversx-sc-modules]
version = "0.64.0"
| 模块 | 目的 | 导入路径 |
|---|---|---|
only_admin |
仅管理员访问控制(所有者可以添加/移除管理员) | multiversx_sc_modules::only_admin |
pause |
可暂停合约模式(#[endpoint] pause / unpause) |
multiversx_sc_modules::pause |
default_issue_callbacks |
标准 ESDT 代币发行/设置角色回调 | multiversx_sc_modules::default_issue_callbacks |
esdt |
代币发行、铸造、燃烧通过统一的 issue_token |
multiversx_sc_modules::esdt |
governance |
DAO 提案、投票和执行 | multiversx_sc_modules::governance |
bonding_curve |
代币定价与绑定曲线公式 | multiversx_sc_modules::bonding_curve |
token_merge |
NFT/SFT 合并和拆分 | multiversx_sc_modules::token_merge |
subscription |
循环支付订阅 | multiversx_sc_modules::subscription |
staking |
基础质押逻辑与奖励 | multiversx_sc_modules::staking |
features |
合约能力的功能标志 | multiversx_sc_modules::features |
users |
用户ID映射(地址到数字ID) | multiversx_sc_modules::users |
ongoing_operation |
长运行操作检查点 | multiversx_sc_modules::ongoing_operation |
claim_developer_rewards |
领取累积的开发者奖励 | multiversx_sc_modules::claim_developer_rewards |
dns |
MultiversX DNS herotag 注册 | multiversx_sc_modules::dns |
用法示例:
#[multiversx_sc::contract]
pub trait MyContract:
multiversx_sc_modules::only_admin::OnlyAdminModule
+ multiversx_sc_modules::pause::PauseModule
+ multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule
{
#[endpoint]
fn admin_action(&self) {
self.require_caller_is_admin();
self.require_not_paused();
// ...
}
}
命名约定
| 项目 | 约定 | 示例 |
|---|---|---|
| 合约 crate | kebab-case | liquidity-pool |
| 模块文件 | snake_case | storage.rs, helpers.rs |
| 存储键 | camelCase | "totalSupply", "feeRate" |
| 错误常量 | SCREAMING_SNAKE | ERROR_INVALID_AMOUNT |
| 模块特质 | PascalCase | StorageModule, ValidationModule |
| 端点名称 | snake_case | fn deposit_tokens(&self) |
| 视图名称 | camelCase(ABI) | #[view(getBalance)] |