home / skills / multiversx / mx-ai-skills / mvx_sharp_edges

mvx_sharp_edges skill

/antigravity/skills/mvx_sharp_edges

This skill helps you spot sharp edge patterns in MultiversX contracts, including async callbacks, gas, storage mappers, decimals, upgrades, and block timing.

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

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

Files (1)
SKILL.md
2.2 KB
---
name: mvx_sharp_edges
description: Identify weird behaviors in WASM, Gas limits, and async call failures specific to MultiversX.
---

# MultiversX Sharp Edges

This skill alerts you to non-obvious behaviors, "gotchas," and platform-specific quirks that often lead to bugs.

## 1. Async Callbacks & Reverts
- **The Edge**: When an Async Call fails, the `#[callback]` is executed.
- **The Sharp Logic**:
    - If you don't implement a callback, the funds return to the contract, but state changes in the original transaction are **NOT reverted** automatically.
    - *Mitigation*: You must manually revert state in the callback if the sub-call failed (or use the synchronous `back_transfers` carefully).

## 2. Gas Limits & Out of Gas (OOG)
- **The Edge**: OOG raises a specific error but can leave the system in a partial state if not handled.
- **The Sharp Logic**:
    - **Cross-shard OOG**: If the destination shard runs OOG, the transaction fails, but the sender shard has already processed the transfer. The callback is triggered with an error.

## 3. Storage Mappers vs Rust Types
- **The Edge**: `VecMapper` is NOT a `Vec`.
- **The Sharp Logic**:
    - `Vec` loads everything into WASM memory (RAM spike).
    - `VecMapper` loads nothing until you call `get(i)`.
    - *Bug*: Using `Vec` for a list of all users = OOG when userbase grows.

## 4. Token Decimal Precision
- **The Edge**: ESDTs can have 0-18 decimals.
- **The Sharp Logic**:
    - Hardcoding `10^18` for "One Token" is wrong.
    - Always fetch `decimals` or require standard 18-decimal tokens.

## 5. Upgradeability
- **The Edge**: `#[init]` is NOT called on upgrade. `#[upgrade]` is called.
- **The Sharp Logic**:
    - If you add a new storage mapper, you must initialize it in `#[upgrade]`.
    - Changing the storage layout of existing mappers (e.g. `struct` field order) corrupts data.

## 6. Block Info inside Views
- **The Edge**: `get_block_timestamp_millis()` / `get_block_timestamp_seconds()` in a `#[view]` (off-chain simulation) might return 0 or a different value than on-chain. Since Supernova (0.6s rounds), prefer `get_block_timestamp_millis()` with `TimestampMillis` for sub-second precision.
- **The Sharp Logic**:
    - Don't rely on block info for critical off-chain view logic.

Overview

This skill surfaces non-obvious MultiversX platform quirks that commonly cause bugs and production incidents. It highlights sharp edges around async callbacks, gas behavior, storage mappers, token decimals, upgradeability, and view-mode block info. Use it to catch subtle failure modes early and design safer contracts.

How this skill works

The skill inspects common code patterns and runtime behaviors and explains the platform-specific outcome you should expect. It focuses on async call semantics and callback execution, cross-shard and local OOG consequences, differences between storage mappers and Rust types, correct token decimal handling, upgrade-time initialization, and block timestamp behavior in views. For each area it gives the faulty pattern, the concrete consequence, and a practical mitigation.

When to use it

  • Designing or reviewing MultiversX smart contracts before deployment
  • Investigating unexpected state changes after async calls or failed sub-calls
  • Auditing gas usage and cross-shard transaction flow
  • Preparing an upgrade or adding new storage mappers
  • Implementing token handling and unit conversions for ESDTs

Best practices

  • Always implement #[callback] to detect sub-call failures and explicitly revert or correct state when needed
  • Use VecMapper for large or unbounded collections to avoid loading all entries into WASM memory
  • Fetch and use the token decimals from the token metadata instead of hardcoding 10^18
  • Handle OOG explicitly: detect callback errors and design idempotent or compensating logic for partial execution
  • Initialize new storage fields in #[upgrade] and avoid changing existing mapper layouts; treat storage layout changes as irreversible

Example use cases

  • A contract issues an async transfer to another contract — use the callback to revert local state if the transfer failed
  • Migrating a contract that adds a user list — use VecMapper to prevent WASM memory spikes
  • Processing ESDT payments; read token decimals to compute amounts and prevent off-by-decimal bugs
  • Post-upgrade initialization that sets defaults for newly introduced mappers in #[upgrade]
  • Debugging a failed cross-shard call where the sender processed a transfer but the destination ran out of gas; check the callback error

FAQ

If an async call fails, do I get an automatic revert?

No. The callback runs, but you must explicitly revert or undo state changes there. Funds may be returned to the contract, but original transaction state is not auto-reverted.

When should I prefer VecMapper over Vec?

Use VecMapper for large or growing lists to avoid loading everything into WASM RAM. Vec is fine for small, fixed-size collections.