multiversx-payment-handling

Handle payments in MultiversX smart contracts. Use when receiving, validating, or routing EGLD/ESDT payments via self.call_value(), Payment types, or payable endpoints. Covers single, multi, optional, and mixed payment patterns.

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-payment-handling" with this command: npx skills add multiversx/mx-ai-skills/multiversx-mx-ai-skills-multiversx-payment-handling

MultiversX Payment Handling — self.call_value() API Reference

Complete reference for receiving and validating payments in MultiversX smart contracts (SDK v0.64+).

Payment Type Hierarchy

Payment<M>              ← v0.64+ preferred, uses NonZeroBigUint (amount guaranteed > 0)
├── token_identifier: TokenId<M>           (unified: EGLD + ESDT)
├── token_nonce: u64
└── amount: NonZeroBigUint<M>

EsdtTokenPayment<M>     ← ESDT-only (no EGLD), BigUint amount (can be 0 in theory)
├── token_identifier: EsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>

EgldOrEsdtTokenPayment<M> ← Legacy mixed type
├── token_identifier: EgldOrEsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>

PaymentVec<M> = ManagedVec<M, Payment<M>>  ← List of payments
FungiblePayment<M>                          ← Payment with nonce == 0 guaranteed

Payment Methods

// Payment<M>
payment.is_fungible() -> bool
payment.fungible_or_panic() -> FungiblePayment<M>
payment.into_tuple() -> (TokenId<M>, u64, NonZeroBigUint<M>)
payment.as_egld_or_esdt_payment() -> &EgldOrEsdtTokenPayment<M>
payment.map_egld_or_esdt(ctx, for_egld, for_esdt) -> U

// NonZeroBigUint<M>
NonZeroBigUint::new(bu: BigUint<M>) -> Option<Self>      // None if zero
NonZeroBigUint::new_or_panic(bu: BigUint<M>) -> Self      // panics if zero
nzbu.into_big_uint() -> BigUint<M>
nzbu.as_big_uint() -> &BigUint<M>

self.call_value() Methods

EGLD-Only

MethodReturnsBehavior
.egld()ManagedRef<BigUint>Accepts EGLD only, panics if ESDT sent. Handles both direct and multi-transfer EGLD.
.egld_decimal()ManagedDecimal<EgldDecimals>EGLD as 18-decimal ManagedDecimal
.egld_direct_non_strict()ManagedRef<BigUint>Raw EGLD from VM. Returns 0 even if ESDT was sent. Low-level, rarely needed.

Single Token (Any Type)

MethodReturnsBehavior
.single()Ref<Payment>Exactly 1 transfer (EGLD or ESDT). Panics if 0 or 2+. Preferred for v0.64+.
.single_optional()Option<Ref<Payment>>0 or 1 transfer. Panics if 2+.
.single_esdt()Ref<EsdtTokenPayment>Exactly 1 ESDT. Panics if EGLD or count != 1.
.single_fungible_esdt()(ManagedRef<EsdtTokenIdentifier>, ManagedRef<BigUint>)Exactly 1 fungible ESDT (nonce == 0).
.egld_or_single_esdt()EgldOrEsdtTokenPayment0 or 1 transfer. Returns EGLD(0) if nothing sent.
.egld_or_single_fungible_esdt()(EgldOrEsdtTokenIdentifier, BigUint)Like above but panics if non-fungible.

Multi-Token

MethodReturnsBehavior
.all()ManagedRef<PaymentVec>Recommended. All transfers as Payment list. Handles EGLD + ESDT uniformly.
.all_transfers()ManagedRef<ManagedVec<EgldOrEsdtTokenPayment>>All transfers as legacy type.
.all_esdt_transfers()ManagedRef<ManagedVec<EsdtTokenPayment>>ESDT only. Panics if EGLD present in multi-transfer.

Fixed-Count Arrays

MethodReturnsBehavior
.array::<N>()[Ref<Payment>; N]Exactly N transfers (any type). Panics if count != N.
.multi_esdt::<N>()[Ref<EsdtTokenPayment>; N]Exactly N ESDT transfers. Rejects EGLD.
.multi_egld_or_esdt::<N>()[Ref<EgldOrEsdtTokenPayment>; N]Exactly N transfers (legacy type).

Deprecated — Do NOT Use

DeprecatedReplacementSince
.egld_value().egld()v0.55 — doesn't handle multi-transfer EGLD properly
.any_payment().all()v0.64 — legacy EGLD-or-multi split no longer meaningful

Common Patterns

Single Payment Endpoint

#[payable]
#[endpoint(deposit)]
fn deposit(&self) {
    let payment = self.call_value().single();
    // payment.token_identifier, payment.token_nonce, payment.amount (NonZeroBigUint)
    self.deposits(&self.blockchain().get_caller())
        .update(|total| *total += payment.amount.as_big_uint());
}

EGLD-Only Endpoint

#[payable("EGLD")]
#[endpoint(delegate)]
fn delegate(&self) {
    let payment = self.call_value().egld().clone_value();
    require!(payment >= MIN_EGLD, "Below minimum");
}

Multi-Payment with Validation

#[payable]
#[endpoint(repay)]
fn repay(&self) {
    let payments = self.call_value().all();
    for payment in payments.iter() {
        require!(
            self.is_accepted_token(&payment.token_identifier),
            "Token not accepted"
        );
        self.process_repayment(&payment);
    }
}

Fixed Two-Token Swap

#[payable]
#[endpoint(addLiquidity)]
fn add_liquidity(&self) {
    let [token_a, token_b] = self.call_value().array::<2>();
    require!(token_a.token_identifier != token_b.token_identifier, "Same token");
}

Optional Payment (Claim or Deposit)

#[payable]
#[endpoint(interact)]
fn interact(&self) {
    match self.call_value().single_optional() {
        Some(payment) => self.handle_deposit(&payment),
        None => self.handle_claim(),
    }
}

EGLD-or-ESDT with Token Routing

#[payable]
#[endpoint(addRewards)]
fn add_reward(&self) {
    let payment = self.call_value().egld_or_single_esdt();
    let pool = self.pool_for_token(&payment.token_identifier);
    self.tx().to(&pool)
        .typed(PoolProxy)
        .deposit()
        .payment(payment)
        .returns(ReturnsResult)
        .sync_call();
}

Payment to Transfer Syntax

// Transfer single Payment to caller
let caller = self.blockchain().get_caller();
self.tx().to(&caller).payment(payment.clone()).transfer();

// Transfer EGLD
self.tx().to(&caller).egld(&amount).transfer();

Anti-Patterns

// BAD: using deprecated egld_value — misses EGLD in multi-transfer
let value = self.call_value().egld_value(); // ← DEPRECATED since v0.55

// GOOD: use egld() which handles both direct and multi-transfer
let value = self.call_value().egld();

// BAD: using any_payment — legacy split
let payment = self.call_value().any_payment(); // ← DEPRECATED since v0.64

// GOOD: use all() for uniform handling
let payments = self.call_value().all();

// BAD: not validating token before processing
let payment = self.call_value().single();
self.do_something(&payment); // What if wrong token?

// GOOD: validate token identity
let payment = self.call_value().single();
require!(
    payment.token_identifier == self.accepted_token().get(),
    "Wrong token"
);

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-smart-contracts

No summary provided by upstream source.

Repository SourceNeeds Review
General

multiversx-protocol-experts

No summary provided by upstream source.

Repository SourceNeeds Review
General

multiversx-constant-time

No summary provided by upstream source.

Repository SourceNeeds Review