multiversx-factory-manager

Factory pattern for deploying and managing child contracts from a parent manager. Use when building marketplaces, launchpads, multi-tenant systems, or any protocol that deploys child contracts from a template.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "multiversx-factory-manager" with this command: npx skills add multiversx/mx-ai-skills/multiversx-mx-ai-skills-multiversx-factory-manager

MultiversX Factory/Manager Pattern

Deploy, track, and upgrade child contracts from a template — the three core operations of a factory manager.

What Problem Does This Solve?

When a protocol needs to deploy multiple instances of the same contract (e.g., one per user, per market, per pool), a factory manager handles the deployment lifecycle: deploy from a template, track all instances, and upgrade them when the template changes.

When to Use

ScenarioUse Factory?
Multiple instances of same contract neededYes
Each user/entity gets their own contractYes
Single contract serves all usersNo
Different contract types per deploymentNo — use separate deploy logic

Core Concept

Manager Contract
├── Stores template address (code to clone)
├── Deploy: creates child from template via from_source()
├── Track: registry of all deployed children
└── Upgrade: applies new template to existing children

Operation 1: Deploy

Deploy a new child contract using from_source() to clone the template:

#[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;

    // Deploy from template
    let new_address = self.tx()
        .typed(child_proxy::ChildProxy) // ChildProxy generated from child contract's #[multiversx_sc::proxy]
        .init(init_arg_a, init_arg_b)
        .from_source(template)
        .code_metadata(metadata)
        .returns(ReturnsNewManagedAddress)
        .sync_call();

    // Register in tracking
    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 Explained

FlagEffectWhen to Use
UPGRADEABLEChild can be upgraded laterAlmost always — needed for Operation 3
READABLEOther contracts can read child's storageWhen using cross-contract storage reads
PAYABLEChild can receive EGLD directlyWhen child needs to accept payments
PAYABLE_BY_SCOnly other SCs can pay childWhen child should only interact with contracts

Operation 2: Track (Registry)

#[multiversx_sc::module]
pub trait RegistryModule {
    // Template to clone from
    #[storage_mapper("templateAddress")]
    fn template_address(&self) -> SingleValueMapper<ManagedAddress>;

    // Set of all deployed child addresses
    #[storage_mapper("childAddresses")]
    fn child_addresses(&self) -> SetMapper<ManagedAddress>;

    // Lookup: ID → child address
    #[storage_mapper("childById")]
    fn child_by_id(&self, id: &ManagedBuffer) -> SingleValueMapper<ManagedAddress>;

    // Admin set (who can deploy/upgrade)
    #[storage_mapper("admins")]
    fn admins(&self) -> UnorderedSetMapper<ManagedAddress>;

    // Helper: verify address is a managed child
    fn require_managed_child(&self, address: &ManagedAddress) {
        require!(
            self.child_addresses().contains(address),
            "Not a managed child contract"
        );
    }
}

Pattern: SetMapper for the full set (iteration + contains check) + SingleValueMapper for ID-based lookup. This gives you both O(1) lookup by ID and iteration over all children.

Operation 3: Upgrade

Upgrade a single child to the current template:

#[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 generated from child contract's #[multiversx_sc::proxy]
        .upgrade()
        .code_metadata(metadata)
        .from_source(self.template_address().get())
        .upgrade_async_call_and_exit();
}

Note: When the child contract proxy is not available (e.g., template-based deployment from external code), use raw_deploy() instead of typed() for deployment.

Template Management

#[only_owner]
#[endpoint(setTemplate)]
fn set_template(&self, template_address: ManagedAddress) {
    require!(
        self.blockchain().is_smart_contract(&template_address),
        "Address is not a smart contract"
    );
    self.template_address().set(template_address);
}

Admin Hierarchy

Owner (deployer of manager)
  └── Admins (can deploy/upgrade children)
       └── Users (interact with their own child)
fn require_admin(&self) {
    let caller = self.blockchain().get_caller();
    require!(
        self.admins().contains(&caller)
            || caller == self.blockchain().get_owner_address(),
        "Not authorized"
    );
}

Anti-Patterns

1. No Registry Tracking

// WRONG — deploying without tracking makes upgrades impossible
let addr = self.tx().typed(ChildProxy).init().from_source(template).sync_call();
// addr is lost after this transaction!

2. Missing UPGRADEABLE Flag

// WRONG — child can never be upgraded
let metadata = CodeMetadata::READABLE | CodeMetadata::PAYABLE;
// Should include CodeMetadata::UPGRADEABLE

3. Upgrading Without Validation

// WRONG — no check that address is actually a managed child
fn upgrade(&self, address: ManagedAddress) {
    self.tx().to(address).typed(ChildProxy).upgrade()
        .from_source(self.template_address().get())
        .upgrade_async_call_and_exit();
}

Template

#[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>;
}

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

multiversx-clarification-expert

No summary provided by upstream source.

Repository SourceNeeds Review
General

multiversx-protocol-experts

No summary provided by upstream source.

Repository SourceNeeds Review
General

multiversx-smart-contracts

No summary provided by upstream source.

Repository SourceNeeds Review
General

multiversx-spec-compliance

No summary provided by upstream source.

Repository SourceNeeds Review