multiversx-payment-handling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

MultiversX Payment Handling —
self.call_value()
API Reference

MultiversX 支付处理 —
self.call_value()
API 参考文档

Complete reference for receiving and validating payments in MultiversX smart contracts (SDK v0.64+).
MultiversX智能合约中接收和验证支付的完整参考(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<M>              ← v0.64+ 推荐使用,基于NonZeroBigUint(金额保证>0)
├── token_identifier: TokenId<M>           (统一支持:EGLD + ESDT)
├── token_nonce: u64
└── amount: NonZeroBigUint<M>

EsdtTokenPayment<M>     ← 仅支持ESDT(不支持EGLD),金额为BigUint(理论上可为0)
├── token_identifier: EsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>

EgldOrEsdtTokenPayment<M> ← 旧版混合类型
├── token_identifier: EgldOrEsdtTokenIdentifier<M>
├── token_nonce: u64
└── amount: BigUint<M>

PaymentVec<M> = ManagedVec<M, Payment<M>>  ← 支付列表
FungiblePayment<M>                          ← 保证nonce == 0的支付

Payment Methods

支付方法

rust
// 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>
rust
// 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>      // 若为0则返回None
NonZeroBigUint::new_or_panic(bu: BigUint<M>) -> Self      // 若为0则触发panic
nzbu.into_big_uint() -> BigUint<M>
nzbu.as_big_uint() -> &BigUint<M>

self.call_value()
Methods

self.call_value()
方法

EGLD-Only

仅支持EGLD

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.
方法返回值行为
.egld()
ManagedRef<BigUint>
仅接受EGLD,若传入ESDT则触发panic。支持直接转账和多笔转账的EGLD。
.egld_decimal()
ManagedDecimal<EgldDecimals>
以18位小数
ManagedDecimal
格式返回EGLD
.egld_direct_non_strict()
ManagedRef<BigUint>
从VM获取原始EGLD金额。即使传入ESDT也返回0。底层方法,很少需要使用。

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()
EgldOrEsdtTokenPayment
0 or 1 transfer. Returns EGLD(0) if nothing sent.
.egld_or_single_fungible_esdt()
(EgldOrEsdtTokenIdentifier, BigUint)
Like above but panics if non-fungible.
方法返回值行为
.single()
Ref<Payment>
恰好1笔转账(EGLD或ESDT)。若为0或2笔及以上则触发panic。v0.64+推荐使用。
.single_optional()
Option<Ref<Payment>>
0或1笔转账。若为2笔及以上则触发panic。
.single_esdt()
Ref<EsdtTokenPayment>
恰好1笔ESDT转账。若为EGLD或数量不等于1则触发panic。
.single_fungible_esdt()
(ManagedRef<EsdtTokenIdentifier>, ManagedRef<BigUint>)
恰好1笔可 fungible ESDT(nonce == 0)。
.egld_or_single_esdt()
EgldOrEsdtTokenPayment
0或1笔转账。若未传入任何金额则返回EGLD(0)。
.egld_or_single_fungible_esdt()
(EgldOrEsdtTokenIdentifier, BigUint)
如上,但如果是非fungible代币则触发panic。

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.
方法返回值行为
.all()
ManagedRef<PaymentVec>
推荐使用。 所有转账以
Payment
列表形式返回。统一处理EGLD和ESDT。
.all_transfers()
ManagedRef<ManagedVec<EgldOrEsdtTokenPayment>>
所有转账以旧版类型返回。
.all_esdt_transfers()
ManagedRef<ManagedVec<EsdtTokenPayment>>
仅支持ESDT。若多笔转账中包含EGLD则触发panic。

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).
方法返回值行为
.array::<N>()
[Ref<Payment>; N]
恰好N笔转账(任意类型)。若数量不等于N则触发panic。
.multi_esdt::<N>()
[Ref<EsdtTokenPayment>; N]
恰好N笔ESDT转账。拒绝EGLD。
.multi_egld_or_esdt::<N>()
[Ref<EgldOrEsdtTokenPayment>; N]
恰好N笔转账(旧版类型)。

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
已废弃方法替代方案废弃版本
.egld_value()
.egld()
v0.55 — 无法正确处理多笔转账的EGLD
.any_payment()
.all()
v0.64 — 旧版EGLD/多代币拆分已无意义

Common Patterns

常见模式

Single Payment Endpoint

单笔支付端点

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

仅支持EGLD的端点

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

Multi-Payment with Validation

带验证的多笔支付

rust
#[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);
    }
}
rust
#[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),
            "代币不被接受"
        );
        self.process_repayment(&payment);
    }
}

Fixed Two-Token Swap

固定双代币兑换

rust
#[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");
}
rust
#[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, "不能是相同代币");
}

Optional Payment (Claim or Deposit)

可选支付(领取或存入)

rust
#[payable]
#[endpoint(interact)]
fn interact(&self) {
    match self.call_value().single_optional() {
        Some(payment) => self.handle_deposit(&payment),
        None => self.handle_claim(),
    }
}
rust
#[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

EGLD或ESDT的代币路由

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

支付转转账语法

rust
// 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();
rust
// 将单笔Payment转账给调用者
let caller = self.blockchain().get_caller();
self.tx().to(&caller).payment(payment.clone()).transfer();

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

Anti-Patterns

反模式

rust
// 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"
);
rust
// 错误:使用已废弃的egld_value — 会遗漏多笔转账中的EGLD
let value = self.call_value().egld_value(); // ← 自v0.55起已废弃

// 正确:使用egld(),支持直接转账和多笔转账
let value = self.call_value().egld();

// 错误:使用any_payment — 旧版拆分方式
let payment = self.call_value().any_payment(); // ← 自v0.64起已废弃

// 正确:使用all()进行统一处理
let payments = self.call_value().all();

// 错误:处理前未验证代币
let payment = self.call_value().single();
self.do_something(&payment); // 如果是错误代币怎么办?

// 正确:验证代币合法性
let payment = self.call_value().single();
require!(
    payment.token_identifier == self.accepted_token().get(),
    "错误代币"
);