name: multiversx-payment-handling
description: 在 MultiversX 智能合约中处理支付。用于通过 self.call_value()、支付类型或可支付端点接收、验证或路由 EGLD/ESDT 支付。涵盖单笔、多笔、可选和混合支付模式。
MultiversX 支付处理 — self.call_value() API 参考
MultiversX 智能合约(SDK v0.64+)中接收和验证支付的完整参考。
支付类型层次结构
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<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
NonZeroBigUint::new_or_panic(bu: BigUint<M>) -> Self // 如果为零则 panic
nzbu.into_big_uint() -> BigUint<M>
nzbu.as_big_uint() -> &BigUint<M>
self.call_value() 方法
仅 EGLD
| 方法 |
返回 |
行为 |
.egld() |
ManagedRef<BigUint> |
仅接受 EGLD,如果发送 ESDT 则 panic。处理直接和多转账 EGLD。 |
.egld_decimal() |
ManagedDecimal<EgldDecimals> |
EGLD 作为 18 十进制 ManagedDecimal |
.egld_direct_non_strict() |
ManagedRef<BigUint> |
来自 VM 的原始 EGLD。即使发送 ESDT 也返回 0。低级,很少需要。 |
单笔代币(任何类型)
| 方法 |
返回 |
行为 |
.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 笔可互换 ESDT(nonce == 0)。 |
.egld_or_single_esdt() |
EgldOrEsdtTokenPayment |
0 或 1 笔转账。如果未发送则返回 EGLD(0)。 |
.egld_or_single_fungible_esdt() |
(EgldOrEsdtTokenIdentifier, BigUint) |
类似上述,但如果不可互换则 panic。 |
多代币
| 方法 |
返回 |
行为 |
.all() |
ManagedRef<PaymentVec> |
推荐。 所有转账作为 Payment 列表。统一处理 EGLD + ESDT。 |
.all_transfers() |
ManagedRef<ManagedVec<EgldOrEsdtTokenPayment>> |
所有转账作为遗留类型。 |
.all_esdt_transfers() |
ManagedRef<ManagedVec<EsdtTokenPayment>> |
仅 ESDT。如果在多转账中存在 EGLD 则 panic。 |
固定数量数组
| 方法 |
返回 |
行为 |
.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 笔转账(遗留类型)。 |
已弃用 — 请勿使用
| 已弃用 |
替代 |
自版本 |
.egld_value() |
.egld() |
v0.55 — 无法正确处理多转账 EGLD |
.any_payment() |
.all() |
v0.64 — 遗留 EGLD 或多拆分不再有意义 |
常见模式
单笔支付端点
#[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 端点
#[payable("EGLD")]
#[endpoint(delegate)]
fn delegate(&self) {
let payment = self.call_value().egld().clone_value();
require!(payment >= MIN_EGLD, "低于最小值");
}
多支付验证
#[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);
}
}
固定两代币交换
#[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, "相同代币");
}
可选支付(申领或存款)
#[payable]
#[endpoint(interact)]
fn interact(&self) {
match self.call_value().single_optional() {
Some(payment) => self.handle_deposit(&payment),
None => self.handle_claim(),
}
}
EGLD 或 ESDT 代币路由
#[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();
}
支付到转账语法
// 将单笔支付转账给调用者
let caller = self.blockchain().get_caller();
self.tx().to(&caller).payment(payment.clone()).transfer();
// 转账 EGLD
self.tx().to(&caller).egld(&amount).transfer();
反模式
// 错误:使用已弃用的 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(),
"错误代币"
);