Unsafe Operations on SpaceVM - MIP-26

A Framework for Fallible Operations in the SpaceVM

Section 1: The Execution and Reversion Model of the SpaceVM

1.1 The Stateless Execution Environment

A defining characteristic of the MultiversX SpaceVM is its statelessness during the execution process.1 When a smart contract is invoked, it is not permitted to modify the global blockchain state directly. All proposed changes—such as updates to account balances, modifications to contract storage, or the creation of new tokens—are not immediately committed to the state trie. Instead, the MultiversX Environment Interface (EI), which serves as the bridge between the smart contract’s WASM code and the underlying blockchain, accumulates these state modifications within a transient data structure.

This design choice carries profound architectural implications. It dramatically simplifies the process of error handling: the entire set of proposed changes, held in this temporary, isolated buffer, is simply discarded. Only upon the successful completion of the entire execution flow is this transient data structure validated and atomically applied to the blockchain’s state.1

This model ensures that every state transition is atomic; it either succeeds completely or fails completely, leaving the prior state intact. This obviates entire classes of bugs and vulnerabilities related to partial state updates. However, it also means that the FailExecution mechanism, which triggers the discard of this transient data, is an all-or-nothing proposition.

1.2 The MultiversX Environment Interface (EI) and High-Level Error Handling

Smart contract developers on the MultiversX network primarily interact with the VM through a sophisticated Rust framework.2 This framework provides a high-level, ergonomic abstraction over the low-level WASM environment and the VM hooks. A critical part of this framework is its approach to error handling and preconditions. Developers are provided with powerful macros, notably

sc_panic! and require!, to manage control flow and enforce logical constraints within their contracts.4

The require! macro is the most common tool for validating preconditions. A typical use case is require!(condition, “error message”), which checks if a given boolean expression evaluates to true. If it evaluates to false, the execution is immediately halted, and the provided error message is surfaced. This macro is effectively syntactic sugar for a more direct conditional panic: if!condition { sc_panic!(“error message”) }.

The sc_panic! macro is the ultimate source of a contract-initiated failure. When invoked, it signals to the VM host that an unrecoverable error has occurred from the contract’s perspective. The VM host interprets this signal and initiates the FailExecution process. This creates a direct and unambiguous link between a logical assertion within a smart contract (e.g., checking if a caller is the owner of an asset) and a full, hard reversion of the transaction.

1.3 The Anatomy of FailExecution

The FailExecution call is the VM’s definitive “kill switch” for a smart contract execution. As described in the initial query, its consequences are absolute:

  1. Execution Halt: The VM immediately stops processing any further opcodes within the smart contract.

  2. State Reversion: The entire transient data structure, containing all proposed state changes from the beginning of the transaction, is discarded. The blockchain state reverts to precisely what it was before the transaction began.

  3. Transaction Failure: The transaction is marked with a “failed” status on the blockchain.

  4. Gas Consumption: The gas provided for the transaction is consumed up to the point of the FailExecution call. This ensures that the network is compensated for the computational resources used, preventing denial-of-service attacks via transactions that are designed to fail.

  5. Event Emission: A signalError event is generated, which can be indexed and observed by off-chain services, indicating that a contract call failed and providing context such as the caller’s address.5

This mechanism is an exceptionally robust safety feature. It acts as a powerful backstop, preventing any contract from committing an invalid or inconsistent state transition to the blockchain. However, its nature as a blunt instrument is its primary drawback. It conflates fundamentally different categories of errors.

This rigidity becomes particularly problematic in the context of contract composability. The promise of a rich dApp ecosystem rests on the ability of contracts to interact with and build upon one another. If Contract A makes a call to Contract B, and Contract B fails for a non-critical, temporary reason (e.g., a resource it needed was momentarily locked by another transaction), the FailExecution model dictates that the entire execution stack is unwound. Not only does Contract B’s work get reverted, but all of Contract A’s work up to that point is also lost. This creates a fragile dependency chain, where the failure of any single component leads to the failure of the whole system.

Section 2: A Proposed Architecture for Returnable VM Errors (Fallible Hooks)

To address the limitations of the monolithic FailExecution model, a more nuanced error-handling architecture is required. This section proposes a framework for introducing fallible VM hooks, which can return actionable error codes to the calling smart contract without causing a complete transaction reversion. This architecture is not a replacement for the existing security model but an extension of it, designed to enhance the expressiveness and resilience of smart contracts.

2.1 The Principle of Error Classification

The cornerstone of the proposed architecture is the formal classification of VM errors into two distinct categories. This classification provides a clear and defensible rationale for determining when a hard revert is necessary versus when a recoverable error is appropriate.

2.1.1 Class 1: Unrecoverable Errors (Retain FailExecution)

This class encompasses errors that are fundamental, non-negotiable, and indicative of a serious flaw in the contract’s logic, a developer mistake, or a potentially malicious attempt to compromise the VM or protocol integrity. These errors must continue to trigger an immediate and irreversible FailExecution. Allowing a contract to handle these errors would create ambiguity and potential attack vectors.

Examples of Class 1 errors include:

  • Invalid Input Formatting: Passing a syntactically incorrect address, a malformed public key, or arguments of the wrong length or type to a VM hook.

  • Protocol Constraint Violations: Attempting to create a token with an invalid ticker name 8, or trying to access a protected storage key reserved for the system.

  • Logical Impossibilities: Performing an integer division by zero or attempting an out-of-bounds memory access within the VM.

  • Security Boundary Violations: A contract trying to manipulate the storage or code of another contract without authorization.

For these cases, FailExecution remains the only correct response. It protects the chain’s integrity and provides a clear, unmistakable signal to the developer that a critical bug exists in their code.

2.1.2 Class 2: Recoverable, State-Contingent Errors (Transition to Returnable Errors)

This class includes errors that arise not from a flaw in the contract’s code but from the specific state of the blockchain at the moment of execution. These conditions are often temporary or dependent on user actions, and a well-designed contract should be able to anticipate and react to them.

Examples of Class 2 errors include:

  • Insufficient Funds: Attempting to transfer more EGLD or ESDT tokens than an account possesses.

  • Ownership Mismatches: Trying to transfer an NFT that the calling address does not own.

  • Permission Denials: Calling a function that requires a specific role (e.g., ESDTRoleLocalMint) that the caller has not been granted.8

  • State-Based Precondition Failures: Attempting to participate in a sale that has already concluded or trying to claim rewards that are not yet available.

For these state-contingent failures, FailExecution is an overly punitive measure. A returnable error code empowers the contract to handle the situation gracefully, improving both developer flexibility and end-user experience.

2.2 The “Soft-Fail” Execution Flow

The implementation of this new error-handling paradigm follows a “soft-fail” execution flow. This flow maintains the core security principle of the stateless VM while providing the necessary feedback to the smart contract. The process is as follows:

  1. Invocation: A smart contract calls a VM hook that has been designated as “fallible” (e.g., an ESDT transfer function).

  2. Error Detection: The VM hook executes its internal logic and encounters a Class 2 error condition (e.g., the sender’s balance is insufficient).

  3. Error Return: Instead of invoking FailExecution, the hook immediately ceases its operation and returns a specific, non-zero integer error code to its caller (the VM). It may optionally return a small data payload containing additional context (e.g., the required balance).

  4. Partial State Discard: Crucially, the VM discards any transient state changes that were buffered by that specific hook invocation. For example, if the hook had tentatively debited the sender’s account before checking the final balance, that debit is discarded. However, the larger transient state buffer for the entire transaction remains intact.

  5. Return to Contract: The VM returns control to the smart contract, passing along the error code received from the hook.

  6. Programmatic Handling: The smart contract, now aware of the specific failure, can use its remaining gas to execute alternative logic. It can check the error code and decide whether to try a different action, log the event for off-chain systems, or simply terminate its execution path gracefully.

  7. Transaction Conclusion: If the contract handles the error and continues, or simply finishes its execution path without panicking, the transaction is considered successful. The state changes accumulated before and after the soft-failing call are committed to the blockchain.

This flow provides a powerful try/catch-like mechanism at the smart contract level. It preserves the safety of the system by ensuring no invalid state is ever committed while simultaneously breaking the fragile dependency chain of the all-or-nothing FailExecution model.

2.3 Gas Economics of Fallible Hooks

A critical aspect of this architecture is its interaction with the gas economy of the network. A soft-fail operation cannot be free, as this would open a new vector for denial-of-service attacks where a contract could call a failing function in an infinite loop without penalty.

The gas model for fallible hooks must adhere to the following principles:

  • Cost of Work Done: The gas consumed by a VM hook up to the point where it returns an error must be deducted from the transaction’s total gas limit. This includes the cost of reading state, performing checks, and any computation performed before the failure was detected.

  • Deterministic Costing: The gas cost for a failing call must be deterministic and predictable. Developers should be able to reason about the gas implications of both the success and failure paths of their contract logic.

  • Fair Pricing: The cost of a soft-failing hook should be roughly equivalent to the cost of the checks that led to the failure. It should be cheaper than a successful call (which involves writing state) but more expensive than a simple no-op. The gas schedule, which defines costs for various operations 8, would need to be updated to reflect these new execution paths.

By correctly pricing these soft-fail operations, the network ensures that computational resources are paid for, maintaining economic stability while enabling this more expressive and powerful programming model. This architecture is not a proposal to weaken the VM’s security but to enhance its capabilities. By carefully classifying errors and retaining FailExecution for truly critical failures, the fundamental safety guarantees of the protocol are preserved. The introduction of returnable errors for state-contingent conditions is a new layer built upon this secure foundation, providing developers with the tools needed to build the more complex, resilient, and user-friendly applications that will drive the future growth of the MultiversX ecosystem.

Section 3: Granular Analysis of vmhooks Operations

This section provides a detailed, function-by-function analysis of the core VM hooks, which are logically grouped into files such as baseOps.go, esdtOps.go, storageOps.go, and cryptoOps.go within the vmhost/vmhooks directory.10 Although direct access to the latest source code is unavailable 11, the functionality and error conditions of these hooks can be reliably inferred from a combination of developer documentation, protocol release notes, and the structure of related MultiversX repositories. For each function, this analysis identifies existing error conditions that trigger

FailExecution and proposes a new behavior based on the Class 1/Class 2 error classification framework.

3.1 Base Operations (baseOps.go)

These hooks are inferred to manage the most fundamental on-chain actions, such as native currency transfers and contract lifecycle management. Their correct and predictable behavior is paramount to the stability of the entire network.

Function Name (Inferred) Current Error Condition Triggering FailExecution Proposed Behavior Justification
Send The source account has an insufficient EGLD balance to cover the transfer amount. Returnable Error This is the canonical Class 2 error. A contract attempting to make a payment or distribute funds should not be forced into a hard revert if one target account cannot be funded. It should be able to receive a specific ERROR_INSUFFICIENT_FUNDS code, log the failure, and continue processing other payments in the same transaction. This enables robust batch payment and escrow-release mechanisms.
CreateContract A nonce collision occurs for the new contract address, or the creator account does not have sufficient balance for the deployment deposit. Retain FailExecution A nonce collision is a Class 1 error, suggesting a critical issue like a replay attack or a fundamental flaw in the deployment logic. It must cause a hard revert to prevent unintended contract overwrites or state corruption. Insufficient balance for deployment is also treated as a Class 1 error in this specific context, as contract deployment is a foundational, one-time setup action that should only be attempted when all preconditions are definitively met.
GetAccountBalance The target address provided is syntactically invalid (e.g., wrong length, invalid checksum). Retain FailExecution Passing malformed data to the VM is a quintessential Class 1 developer error. The contract has failed a basic sanity check on its inputs. Allowing execution to continue could lead to unpredictable behavior based on how the invalid address is processed further down the line. A hard failure is the safest and most informative response.
GetShardOfAddress The target address provided is syntactically invalid. Retain FailExecution The justification is identical to GetAccountBalance. The integrity of inputs passed to the VM’s core sharding and addressing logic must be strictly enforced with FailExecution. This prevents any ambiguity in state lookups.

3.2 ESDT and NFT Operations (esdtOps.go)

This is arguably the most critical area for the proposed architectural changes, as token interactions are the lifeblood of DeFi, NFT marketplaces, and gaming applications. The ability to handle token-related failures gracefully is essential for building complex economic systems. The functions and their behaviors are inferred from the detailed ESDT documentation 8 and recent development activity, which explicitly mentions testing ESDT multi-transfers with returnable errors.7

Function Name (Inferred) Current Error Condition Triggering FailExecution Proposed Behavior Justification
ESDTNFTTransfer / MultiESDTNFTTransfer The caller does not own the specified NFT/SFT nonce; the token is frozen or non-transferable; or the SFT amount is insufficient. Returnable Error This is a crucial Class 2 set of conditions. A marketplace contract attempting to execute a sale must be able to handle the case where the seller has already transferred the NFT in a separate transaction. A hard revert in this scenario would break the user experience. The contract needs to receive a specific error code (e.g., ERROR_NOT_OWNER, ERROR_TOKEN_FROZEN) to invalidate the specific trade and potentially notify the buyer, without failing the entire batch of trades it might be processing. This change is a prerequisite for building robust, high-throughput NFT exchanges.
ESDTLocalMint / ESDTLocalBurn The caller does not possess the required ESDTRoleLocalMint or ESDTRoleLocalBurn special role. Returnable Error Permissions and roles can be dynamic. A contract might be designed to attempt a minting operation periodically, expecting it to succeed only when a governance vote has granted it the necessary role. A hard FailExecution makes this pattern clumsy and expensive. A returnable ERROR_PERMISSION_DENIED allows the contract to check its permissions by attempting the action, enabling more elegant and gas-efficient designs for DAOs and token controllers.
ESDTNFTCreate The provided token name or ticker contains invalid characters, or the initial properties are non-compliant with protocol rules. Retain FailExecution The rules for token issuance are strict to ensure network-wide compatibility and prevent the creation of malformed or problematic assets.8 Violating these rules is a Class 1 setup error. It indicates a mistake in the deployment script or contract constructor that must be fixed. Failing early and hard prevents non-standard assets from polluting the ecosystem.
SetSpecialRole The caller attempts to assign a role but does not have the role-management authority for that token. Returnable Error This is a Class 2 permission check. While assigning a role to an invalid (e.g., zero) address should be a hard failure (Class 1), the case where a valid caller attempts to grant a valid role to a valid address but simply lacks the permission should be recoverable. This allows a contract to attempt a permission change and react to the failure, which is useful in complex, multi-layered administrative contract setups.

3.3 Storage and State Operations (storageOps.go)

These hooks are fundamental to a smart contract’s ability to persist data across transactions. Their security is non-negotiable, as they guard the integrity of a contract’s internal state.

The primary operations are Load (or StorageGet) and Save (or StorageSet). An important aspect of the current design is that Load operations for non-existent keys already behave in a fallible manner: they simply return an empty or zero-filled buffer rather than causing an error. This is the correct and desired behavior and should be maintained.

The only condition that should trigger a failure in this context is an attempt to access storage outside the contract’s permitted sandbox. This is a critical security boundary. Any attempt to read from or write to a protected key (e.g., keys used by the protocol itself) or the storage of another contract must be treated as a hostile act. Therefore, any such attempt is a Class 1 error, and FailExecution is the only appropriate response. This ensures that contracts remain isolated and cannot interfere with one another’s state, a cornerstone of blockchain security.

3.4 Cryptographic Operations (cryptoOps.go)

The introduction of new cryptographic VM hooks in releases like Spica (v1.8.4.0), including VerifySecp256r1, VerifyBLSSignatureShare, and VerifyBLSMultiSig 12, highlights the growing need for on-chain signature verification. These functions are essential for building multisig wallets, cross-chain bridges, and oracle systems. The utility of these hooks is directly tied to their ability to return a result rather than halting execution.

Function Name (Inferred) Current Error Condition Triggering FailExecution Proposed Behavior Justification
CheckSig / VerifyED25519 The provided signature is invalid for the given message and public key. Returnable Error/Boolean This is a Class 2 condition. The entire purpose of a signature verification function from a smart contract’s perspective is to get an answer (true or false) to the question, Is this signature valid? Forcing a FailExecution on an invalid signature (false) renders the function almost useless for any logic that needs to count valid signatures or handle cases where some signatures in a set are invalid. The hook should return a zero for an invalid signature and a non-zero value for a valid one.
VerifySecp256r1, VerifyBLSSignatureShare, VerifyBLSMultiSig The cryptographic verification of the signature or signature share fails. Returnable Error/Boolean As with the native CheckSig, these advanced cryptographic primitives are tools for decision-making within a contract. A bridge contract needs to check a batch of validator signatures and count how many are valid to reach a consensus threshold. If each invalid signature caused a hard revert, this would be impossible to implement on-chain. These hooks must return a clear, non-aborting success/failure indication.
Keccak256 / Sha256 The input data provided to the hashing function is malformed (e.g., exceeds a VM-defined length limit). Retain FailExecution A hashing function itself does not fail based on the content of the data. A failure in this context would imply an issue with the parameters of the call itself, such as providing a pointer to a buffer that is too large for the VM to handle safely. This is a Class 1 developer error related to resource management and should result in a hard failure to prevent excessive memory consumption or other VM-level instabilities.

A crucial element that emerges from this granular analysis is the need for specificity in the returned error codes. Simply returning a generic 1 for “error” is insufficient. To unlock the full potential of this new architecture, the VM hooks must return distinct error codes for different failure modes. For instance, a failing ESDTNFTTransfer call should return different codes for ERROR_NOT_OWNER, ERROR_TOKEN_FROZEN, and ERROR_INSUFFICIENT_SFT_BALANCE. This granularity allows the calling contract to implement sophisticated, multi-pathed logic (switch statements) to handle each failure case appropriately, transforming the error-handling mechanism from a simple binary check into a rich source of on-chain information.

Section 4: Consolidated Recommendations for Evolving FailExecution

The granular analysis of individual VM hooks reveals several recurring patterns of error conditions. By synthesizing these findings, it is possible to formulate a clear, thematic set of high-level recommendations for evolving the FailExecution model. This approach provides a strategic blueprint for protocol architects, organizing the proposed changes around the underlying nature of the error rather than specific function implementations. The guiding principle remains the consistent application of the Class 1 (unrecoverable) and Class 2 (recoverable) error framework.

4.1 Insufficient Balance/Quantity Conditions

A significant number of potential failures in smart contracts relate to accounts not possessing a sufficient quantity of a given asset, be it the native EGLD currency or a specific ESDT token. Currently, these scenarios typically result in a hard transaction revert.

Recommendation: All checks for insufficient balance or quantity of EGLD, fungible ESDT, or Semi-Fungible Tokens (SFTs) should be converted from FailExecution triggers to returnable errors.

Rationale: This is the most prevalent and intuitive category of recoverable, state-contingent (Class 2) errors. The balance of an account is a highly dynamic value that can change from one block to the next. A contract’s logic should not be considered flawed simply because an account’s balance was lower than expected at the moment of execution. By providing a specific, non-aborting error code (e.g., ERROR_INSUFFICIENT_FUNDS), the VM empowers developers to build far more resilient and practical applications. This change enables patterns such as:

  • Graceful Payment Failures: An e-commerce contract can attempt to charge a user and, upon receiving an insufficient funds error, log the failed order and notify the user to top up their account, rather than the transaction failing opaquely.

  • Batch Processing Resilience: A contract designed to airdrop tokens or pay out dividends to a large list of addresses can continue its loop even if some addresses in the batch cannot receive the funds. It can simply skip the failing transfers and successfully complete the rest.

  • Partial Fills: In a decentralized exchange, a contract could be designed to fill an order up to the available balance, a common feature that is difficult to implement when any shortfall causes a full revert.

Affected Hooks (Inferred): Send, ESDTTransfer, MultiESDTNFTTransfer (for SFTs), ESDTLocalBurn.

4.2 Ownership and Permissions Conditions

The second major category of state-contingent errors involves access control: checking if a caller owns a specific asset (like an NFT) or possesses the necessary rights (special roles) to perform an action.

Recommendation: All checks related to token ownership and the possession of special roles should be converted to returnable errors.

Rationale: Like balances, ownership and permissions are dynamic properties of the blockchain state. They are expected to change as a result of user actions and governance decisions. Treating a failed permission check as a catastrophic (Class 1) error prevents contracts from dynamically probing the state. A contract must be able to attempt an action and handle the result based on the permissions landscape at that exact moment. This is fundamental for:

  • NFT Marketplaces: An offer to buy an NFT should not fail catastrophically if the NFT was sold moments before. The contract should receive an ERROR_NOT_OWNER code, allowing it to invalidate the offer and refund the potential buyer in the same transaction.

  • Decentralized Autonomous Organizations (DAOs): A contract managing DAO proposals may allow certain actions only to be performed by addresses with a specific role. The contract logic should be able to attempt an administrative action and handle the ERROR_PERMISSION_DENIED case gracefully if the caller’s role has been revoked.

  • Automated Role Management: A parent contract could be designed to try to grant a role via a child contract, and if it fails due to its own permissions being revoked, it can enter a fallback state.

Affected Hooks (Inferred): ESDTNFTTransfer, MultiESDTNFTTransfer (for NFTs), ESDTLocalMint, ESDTLocalBurn, SetSpecialRole, UnsetSpecialRole.

4.3 Invalid Input Parameter Conditions

This category serves as the critical counterpoint to the proposed changes. It defines the boundary of what should remain unrecoverable. These are errors that stem not from the state of the world, but from the flawed construction of the call itself.

Recommendation: All checks for fundamentally invalid, malformed, out-of-bounds, or syntactically incorrect input parameters must continue to trigger FailExecution.

Rationale: This is a non-negotiable security and stability measure. The VM’s environment interface is a strict contract, and the smart contract is responsible for upholding its end by providing valid inputs. A failure to do so represents a bug in the smart contract code. Allowing a contract to “handle” an error from passing garbage input would create a dangerous precedent and could lead to unpredictable downstream consequences or resource exhaustion within the VM. Retaining FailExecution for these Class 1 errors:

  • Enforces Developer Discipline: It forces developers to sanitize and validate their inputs before calling the VM, which is a best practice.

  • Protects the VM: It prevents the VM from wasting cycles trying to process nonsensical data or from entering an undefined state.

  • Provides Clear Debugging Signals: A hard revert for an invalid parameter is an unambiguous signal to the developer that their contract’s logic is flawed and needs to be fixed and redeployed.

Affected Hooks (Inferred): GetAccountBalance (with invalid address format), ESDTNFTCreate (with invalid ticker format 8),

Load/Save (with protected/malicious keys), Keccak256 (with out-of-bounds length).

4.4 Cryptographic Verification Conditions

The final category involves the new and increasingly important cryptographic hooks used for signature verification.

Recommendation: All cryptographic verification hooks (CheckSig, VerifySecp256r1, etc.) should return a boolean-like result (e.g., 0 for failure, 1 for success) and must never trigger FailExecution for a simple verification mismatch.

Rationale: The very purpose of these functions, from a smart contract’s point of view, is to answer a question: “Is this signature valid?” The answer is inherently a piece of data (true or false) that the contract needs to continue its logic. Forcing a revert when the answer is false makes the function unusable for its primary on-chain use cases. A multisig contract, for instance, needs to iterate through a list of signatures, check each one, and count the valid ones. This is impossible if the first invalid signature halts the entire transaction. This change is essential for:

  • On-chain Multisig Wallets: Enabling contracts to verify a set of signatures and proceed only if an M-of-N threshold is met.

  • Oracles and Bridges: Allowing contracts to verify data bundles signed by a distributed set of reporters, ignoring any invalid signatures in the bundle.

  • Authentication Schemes: Supporting complex authentication logic where a user might provide one of several possible valid signatures.

Affected Hooks (Inferred): CheckSig, VerifyED25519, VerifySecp256r1, VerifyBLSSignatureShare, VerifyBLSMultiSig.12

Section 5: Implications for the Smart Contract Framework and Developer Tooling

A low-level change to the VM’s error-handling model is of little practical value unless it is exposed to developers through high-level, ergonomic tools. The success of this architectural evolution hinges on corresponding upgrades to the MultiversX Rust smart contract framework (mx-sdk-rs) 3 and the supporting developer toolchain, including

mxpy and the debugger.13 This section details the necessary enhancements to translate the new VM capabilities into tangible power for developers.

5.1 Introducing a Fallible API in the Rust Framework

The current interaction model in the Rust SDK typically involves calling a function that either succeeds (returning its expected value or a unit type ()) or panics, which in turn triggers the VM’s FailExecution. To support the new paradigm, a parallel set of fallible functions must be introduced.

Proposal: For each VM hook that is modified to return a Class 2 error, a new _fallible variant should be added to the corresponding proxy call in the Rust SDK.

For example, the existing API for a direct ESDT transfer might look like this:

self.send().direct(&address, &token_identifier, nonce, &amount);

This call would be complemented by a new fallible version:

let result = self.send().direct_fallible(&address, &token_identifier, nonce, &amount);

The key difference lies in the return type. The _fallible variant would not return () on success. Instead, it would return a standard Rust Result enum, such as Result<(), VmError>.

  • Ok(()): Indicates that the VM hook executed successfully.

  • Err(VmError): Indicates that the VM hook encountered a recoverable error. VmError would be a new enum defined in the framework, mapping directly to the granular error codes returned by the VM.

This design allows developers to use familiar Rust error-handling patterns, such as the match statement or the ? operator, to manage the execution flow.

Example Usage:

A contract could implement logic to handle a transfer failure gracefully:

Rust

let transfer_result = self.send().direct_fallible(&dest, &token, nonce, &amount);

match transfer_result {
Ok(_) => {
// The transfer was successful, proceed with normal logic.
self.emit_successful_transfer_event();
},
Err(e) if e == VmError::InsufficientFunds => {
// The transfer failed due to insufficient funds.
// Instead of reverting, log the failure and continue.
sc_print!("Transfer failed: insufficient funds. User needs to top up.");
self.emit_failed_transfer_event(&dest, "InsufficientFunds");
},
Err(e) => {
// Handle other specific, recoverable VM errors.
sc_panic!("An unexpected but recoverable VM error occurred: {:?}", e);
}
}

This pattern provides immense flexibility, allowing a single transaction to attempt an action, react to its specific outcome, and still succeed.

5.2 Updating the mxpy and Debugging Tools

The introduction of soft-fails creates new execution paths that must be visible to developers for testing and debugging. The existing tooling, including the command-line interface mxpy and the integrated debugger in the Rust framework 3, needs to be updated to understand and visualize these new outcomes.

Proposal: The transaction output and debugging traces must be enhanced to differentiate between a hard FailExecution and a soft-fail (a returned error from a fallible hook).

  • Transaction Logs: When mxpy or an explorer displays the results of a transaction, it should not just show “success” or “fail.” It needs a more nuanced output. A transaction that encountered a soft-fail but handled it and completed successfully should still be marked as “success,” but its logs should clearly indicate the soft-failing call and the error code that was returned.

  • Debugger Visualization: The debugger, which provides a step-by-step trace of contract execution, should use distinct visual cues. For example:

  • A call that triggers FailExecution could be highlighted in red, indicating a terminal, transaction-ending event.

  • A call that returns a recoverable error could be highlighted in yellow, indicating a non-terminal failure, and should display the specific VmError code that was returned to the contract.

  • A successful call would remain in the standard color (e.g., green or white).

These tooling enhancements are not merely cosmetic; they are essential for enabling developers to reason about their contract’s behavior. Without clear visibility into the new error-handling flow, debugging complex interactions involving multiple fallible calls would become exceptionally difficult, undermining the benefits of the underlying VM changes.

Section 6: Conclusion and Strategic Outlook

This report has conducted a comprehensive analysis of the error-handling model within the MultiversX Virtual Machine, identifying the limitations of the current FailExecution-centric paradigm and proposing a detailed architectural evolution towards a more flexible, fallible system. By systematically classifying errors and recommending targeted changes to core VM hooks, this framework outlines a clear path to enhancing the power and resilience of smart contracts on the network. The following conclusions and strategic implications summarize the importance of this initiative.

6.1 Summary of Recommendations

The core proposal is to transition from a purely abort-based error model to a hybrid system that provides developers with greater control over execution flow. This is achieved by adhering to a simple but powerful principle:

  • Retain FailExecution for Class 1 (Unrecoverable) Errors: Any error stemming from a developer mistake, malformed input, or a violation of fundamental protocol security must continue to cause a hard transaction revert. This preserves the ultimate safety net of the VM.

  • Introduce Returnable Errors for Class 2 (State-Contingent) Errors: Any error arising from the dynamic state of the blockchain—such as insufficient balances, ownership mismatches, or permission denials—should be converted to a non-aborting, granular error code returned to the calling contract.

This principle was applied across the spectrum of VM hooks, with key recommendations focused on making balance checks in baseOps, token and role operations in esdtOps, and all cryptographic verifications in cryptoOps fallible. This low-level VM change must be accompanied by corresponding updates to the high-level Rust SDK and developer tooling to ensure the new capabilities are accessible, ergonomic, and debuggable.

6.2 Strategic Impact

The implementation of this framework will have a profound and positive impact on the MultiversX ecosystem, driving growth and innovation across several key areas:

  • Enhanced Composability: This is the most significant benefit. By allowing contracts to handle failures from their dependencies without reverting the entire transaction, the proposed changes will unlock a new generation of sophisticated, interlocking dApps. Complex DeFi strategies, resilient multi-stage NFT marketplaces, and intricate DAO governance systems that are currently difficult or impossible to build safely will become feasible.

  • Improved User Experience (UX): From an end-user’s perspective, fewer transactions will fail opaquely. dApps will be able to provide more graceful error handling, clearly informing users about recoverable issues (e.g., “Insufficient funds, please add more EGLD”) instead of presenting them with a generic “Transaction Failed” message and a wasted gas fee. This leads to a less frustrating and more intuitive user journey.

  • Increased Developer Power and Expressiveness: Granting developers try/catch-like capabilities at the smart contract level is a massive upgrade in expressive power. It moves beyond simple, linear execution paths and allows for complex, state-aware logic. This will attract top-tier development talent to the ecosystem, as they will have the tools to build more robust, reliable, and feature-rich applications that can compete with those on any other leading blockchain platform.

6.3 Final Statement

The evolution from a simple, abort-only error model to a more sophisticated, hybrid architecture that includes fallible operations is a hallmark of a maturing blockchain platform. It signifies a move beyond ensuring only raw protocol safety to enabling the complex application logic required for mainstream adoption. The evidence from recent protocol upgrades 6 and development activity 7 suggests that this evolution is already underway within the MultiversX core team.

By formally adopting and accelerating the framework outlined in this report, MultiversX can systematically implement these changes across the VM, SDK, and tooling. This will not only resolve existing pain points for developers but also preemptively provide the infrastructure needed for the next wave of innovation. This strategic investment in the developer experience will pay significant dividends, solidifying the MultiversX network’s position as a premier, high-performance environment for building the future of decentralized applications.

Works cited

  1. MultiversX Virtual Machine (SpaceVM), accessed August 5, 2025, https://docs.multiversx.com/learn/space-vm/

  2. MultiversX Smart Contracts, accessed August 5, 2025, https://docs.multiversx.com/developers/smart-contracts/

  3. multiversx/mx-sdk-rs: MultiversX SpaceCraft framework for Rust. Contains: a smart contract framework; a complete smart contract build solution; a large collection of example smart contracts; a testing framework and debugger (including a VM model); real blockchain interaction; various other tools. - GitHub, accessed August 5, 2025, https://github.com/multiversx/mx-sdk-rs

  4. Messages - MultiversX Docs, accessed August 5, 2025, https://docs.multiversx.com/developers/developer-reference/sc-messages/

  5. Execution Events - MultiversX Docs, accessed August 5, 2025, https://docs.multiversx.com/developers/event-logs/execution-events/

  6. Releases - MultiversX, accessed August 5, 2025, https://multiversx.com/releases

  7. Activity · multiversx/mx-chain-vm-go - GitHub, accessed August 5, 2025, https://github.com/multiversx/mx-chain-vm-go/activity

  8. Fungible tokens - MultiversX Docs, accessed August 5, 2025, https://docs.multiversx.com/tokens/fungible-tokens/

  9. Constants - MultiversX Docs, accessed August 5, 2025, https://docs.multiversx.com/developers/constants/

  10. mx-chain-vm-v1_4-go/Makefile at master - GitHub, accessed August 5, 2025, https://github.com/multiversx/mx-chain-vm-v1_4-go/blob/master/Makefile

  11. github.com, accessed August 5, 2025, https://github.com/multiversx/mx-chain-vm-go/tree/master/vmhost/vmhooks

  12. Latest Protocol Releases - MultiversX, accessed August 5, 2025, https://multiversx.com/releases/protocol?79283dca_page=2

  13. Builder Tools & Resources - MultiversX, accessed August 5, 2025, https://multiversx.com/builders/builder-tools-resources

Request for all the developers to reply and list all the endpoint they would like to have fallible in case of the unSafeOperation mode and to dissect this proposal as much as possible.