MultiversX工厂管理器模式Skill multiversx-factory-manager

这个技能用于在MultiversX区块链上实现工厂模式,以部署和管理智能合约的子实例。它支持从模板克隆合约、跟踪所有实例以及升级功能,适用于构建去中心化市场、启动板和多租户系统等场景。关键词:区块链,智能合约,工厂模式,MultiversX,部署,管理,升级,DApp开发,智能合约模板。

智能合约 0 次安装 0 次浏览 更新于 3/21/2026

名称: multiversx-factory-manager 描述: 用于从父管理器部署和管理子合约的工厂模式。适用于构建市场、启动板、多租户系统或任何从模板部署子合约的协议。

MultiversX 工厂/管理器模式

从模板部署、跟踪和升级子合约——工厂管理器的三个核心操作。

解决什么问题?

当协议需要部署同一合约的多个实例时(例如,每个用户、市场或池一个),工厂管理器处理部署生命周期:从模板部署、跟踪所有实例以及在模板更改时升级它们。

何时使用

场景 是否使用工厂?
需要同一合约的多个实例
每个用户/实体获得自己的合约
单个合约服务所有用户
每次部署不同合约类型 否—使用单独的部署逻辑

核心概念

管理器合约
├── 存储模板地址(用于克隆的代码)
├── 部署:通过 from_source() 从模板创建子合约
├── 跟踪:所有部署子合约的注册表
└── 升级:将新模板应用于现有子合约

操作 1:部署

使用 from_source() 克隆模板部署新子合约:

#[endpoint(deployChild)]
fn deploy_child(
    &self,
    child_id: ManagedBuffer,
    init_arg_a: BigUint,
    init_arg_b: ManagedAddress,
) -> ManagedAddress {
    let caller = self.blockchain().get_caller();
    let template = self.template_address().get();

    let metadata = CodeMetadata::UPGRADEABLE
        | CodeMetadata::READABLE
        | CodeMetadata::PAYABLE;

    // 从模板部署
    let new_address = self.tx()
        .typed(child_proxy::ChildProxy) // ChildProxy 从子合约的 #[multiversx_sc::proxy] 生成
        .init(init_arg_a, init_arg_b)
        .from_source(template)
        .code_metadata(metadata)
        .returns(ReturnsNewManagedAddress)
        .sync_call();

    // 在跟踪中注册
    self.child_addresses().insert(new_address.clone());
    self.child_by_id(&child_id).set(new_address.clone());

    self.deploy_event(&caller, &child_id, &new_address);
    new_address
}

CodeMetadata 解释

标志 效果 何时使用
UPGRADEABLE 子合约稍后可升级 几乎总是—操作 3 需要
READABLE 其他合约可以读取子合约存储 当使用跨合约存储读取时
PAYABLE 子合约可以直接接收 EGLD 当子合约需要接受付款时
PAYABLE_BY_SC 只有其他 SC 可以支付子合约 当子合约应仅与合约交互时

操作 2:跟踪(注册表)

#[multiversx_sc::module]
pub trait RegistryModule {
    // 克隆的模板
    #[storage_mapper("templateAddress")]
    fn template_address(&self) -> SingleValueMapper<ManagedAddress>;

    // 所有部署子合约地址的集合
    #[storage_mapper("childAddresses")]
    fn child_addresses(&self) -> SetMapper<ManagedAddress>;

    // 查找:ID → 子合约地址
    #[storage_mapper("childById")]
    fn child_by_id(&self, id: &ManagedBuffer) -> SingleValueMapper<ManagedAddress>;

    // 管理员集合(谁可以部署/升级)
    #[storage_mapper("admins")]
    fn admins(&self) -> UnorderedSetMapper<ManagedAddress>;

    // 辅助:验证地址是受管理的子合约
    fn require_managed_child(&self, address: &ManagedAddress) {
        require!(
            self.child_addresses().contains(address),
            "不是受管理的子合约"
        );
    }
}

模式SetMapper 用于完整集合(迭代 + 包含检查)+ SingleValueMapper 用于基于 ID 的查找。这提供了 O(1) 的 ID 查找和所有子合约的迭代。

操作 3:升级

将单个子合约升级到当前模板:

#[endpoint(upgradeChild)]
fn upgrade_child(&self, child_id: ManagedBuffer) {
    self.require_admin();

    let child_address = self.child_by_id(&child_id).get();
    self.require_managed_child(&child_address);

    let metadata = CodeMetadata::UPGRADEABLE
        | CodeMetadata::READABLE
        | CodeMetadata::PAYABLE;

    self.tx()
        .to(child_address)
        .typed(child_proxy::ChildProxy) // ChildProxy 从子合约的 #[multiversx_sc::proxy] 生成
        .upgrade()
        .code_metadata(metadata)
        .from_source(self.template_address().get())
        .upgrade_async_call_and_exit();
}

注意:当子合约代理不可用时(例如,来自外部代码的基于模板部署),请使用 raw_deploy() 而不是 typed() 进行部署。

模板管理

#[only_owner]
#[endpoint(setTemplate)]
fn set_template(&self, template_address: ManagedAddress) {
    require!(
        self.blockchain().is_smart_contract(&template_address),
        "地址不是智能合约"
    );
    self.template_address().set(template_address);
}

管理员层次结构

所有者(管理器部署者)
  └── 管理员(可以部署/升级子合约)
       └── 用户(与自己的子合约交互)
fn require_admin(&self) {
    let caller = self.blockchain().get_caller();
    require!(
        self.admins().contains(&caller)
            || caller == self.blockchain().get_owner_address(),
        "未授权"
    );
}

反模式

1. 无注册表跟踪

// 错误—部署而不跟踪使得升级不可能
let addr = self.tx().typed(ChildProxy).init().from_source(template).sync_call();
// 地址在此交易后丢失!

2. 缺少 UPGRADEABLE 标志

// 错误—子合约永远无法升级
let metadata = CodeMetadata::READABLE | CodeMetadata::PAYABLE;
// 应包含 CodeMetadata::UPGRADEABLE

3. 升级而不验证

// 错误—无检查地址是否实际上是受管理的子合约
fn upgrade(&self, address: ManagedAddress) {
    self.tx().to(address).typed(ChildProxy).upgrade()
        .from_source(self.template_address().get())
        .upgrade_async_call_and_exit();
}

模板

#[multiversx_sc::module]
pub trait FactoryModule {
    #[storage_mapper("templateAddress")]
    fn template_address(&self) -> SingleValueMapper<ManagedAddress>;

    #[storage_mapper("childAddresses")]
    fn child_addresses(&self) -> SetMapper<ManagedAddress>;

    #[storage_mapper("childById")]
    fn child_by_id(&self, id: &ManagedBuffer) -> SingleValueMapper<ManagedAddress>;

    #[storage_mapper("admins")]
    fn admins(&self) -> UnorderedSetMapper<ManagedAddress>;
}