home / skills / multiversx / mx-ai-skills / mvx_flash_loan_patterns

mvx_flash_loan_patterns skill

/antigravity/skills/mvx_flash_loan_patterns

This skill analyzes and enforces the atomic lend-execute-verify pattern, ensuring reentrancy guards, shard validation, and safe repayment in a single

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

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

Files (1)
SKILL.md
2.3 KB
---
name: mvx_flash_loan_patterns
description: Atomic lend-execute-verify pattern — reentrancy guards, shard validation, endpoint checks.
---

# MultiversX Atomic Lend-Execute-Verify Pattern

Lend assets, execute callback, verify repayment — all atomically in one transaction.

## When to Use

- Flash loans, atomic swaps, temporary grants
- NOT for cross-shard operations (breaks atomicity)

## Security Checklist

1. Reentrancy guard — prevent nested operations
2. Shard validation — target must be same shard
3. Endpoint validation — callback must not be built-in
4. Repayment verification — check contract balance after callback
5. Guard cleanup — always clear the flag

## Core Flow

```rust
#[endpoint(atomicOperation)]
fn atomic_operation(&self, asset: TokenIdentifier, amount: BigUint, target: ManagedAddress, callback: ManagedBuffer) {
    self.require_not_ongoing();
    self.require_same_shard(&target);
    require!(!callback.is_empty() && !self.blockchain().is_builtin_function(&callback), "Invalid endpoint");

    let fee = &amount * self.fee_bps().get() / 10_000u64;
    let balance_before = self.blockchain().get_sc_balance(&asset.clone().into(), 0);

    self.operation_ongoing().set(true);
    self.tx().to(&target).raw_call(callback).single_esdt(&asset, 0, &amount).sync_call();

    let balance_after = self.blockchain().get_sc_balance(&asset.into(), 0);
    require!(balance_after >= balance_before + &fee, "Repayment insufficient");

    self.operation_ongoing().set(false);
}
```

## Reentrancy Guard

```rust
#[storage_mapper("operationOngoing")]
fn operation_ongoing(&self) -> SingleValueMapper<bool>;

fn require_not_ongoing(&self) {
    require!(!self.operation_ongoing().get(), "Operation already in progress");
}
```

## Shard Validation

```rust
fn require_same_shard(&self, target: &ManagedAddress) {
    let target_shard = self.blockchain().get_shard_of_address(target);
    let self_shard = self.blockchain().get_shard_of_address(&self.blockchain().get_sc_address());
    require!(target_shard == self_shard, "Must be same shard");
}
```

**Why**: Cross-shard calls execute in different blocks, breaking atomicity.

## Anti-Patterns

- Checking storage value instead of actual `get_sc_balance` for repayment
- No shard validation (cross-shard sync_call becomes async silently)
- Built-in function callbacks bypassing repayment logic

Overview

This skill describes a secure atomic lend-execute-verify pattern for MultiversX smart contracts. It focuses on performing a loan, calling a target callback, and verifying repayment inside one transaction while preventing common pitfalls. The pattern emphasizes reentrancy protection, shard validation, endpoint checks, and strict repayment verification.

How this skill works

Before sending funds, the contract verifies no operation is currently ongoing and that the target address is in the same shard. It sends the funds and invokes the provided callback in a single atomic call, then checks the contract balance to verify repayment plus fees. The guard flag is cleared at the end to allow future operations.

When to use it

  • Flash loans and temporary asset grants requiring atomic execution
  • Atomic swaps where loaned assets must be returned in the same transaction
  • Short-lived liquidity injections for arbitrage or on-chain composability
  • Never for cross-shard interactions or when async callbacks are acceptable

Best practices

  • Use a boolean reentrancy guard to block nested or concurrent operations
  • Validate that the target address is on the same shard to preserve atomicity
  • Reject built-in function callbacks and empty endpoints to avoid bypasses
  • Verify repayment by reading get_sc_balance after the callback, not storage mirrors
  • Always clear the guard flag in success and error paths to avoid lockouts

Example use cases

  • Provide a flash loan to an arbitrage bot that executes and repays within the same block
  • Perform an atomic token swap between two on-shard contracts with fee enforcement
  • Offer temporary liquidity for a contract to execute a tactical operation and return funds immediately
  • Run a grant-to-execute flow where repayment includes a predefined fee basis points

FAQ

Why must the target be in the same shard?

Cross-shard calls execute in different blocks and lose atomicity, so repayment cannot be guaranteed within a single transaction.

Why check contract balance instead of a storage variable?

Storage values can be manipulated or not reflect token balances; get_sc_balance reads actual on-chain asset balance for reliable repayment verification.