MultiversX支付处理Skill multiversx-payment-handling

这个技能用于在 MultiversX 区块链智能合约中处理和验证支付,包括接收、验证和路由 EGLD 及 ESDT 代币的支付。它涵盖了单笔、多笔、可选和混合支付模式,适用于智能合约开发中的支付端点实现。关键词:MultiversX, 智能合约, 支付处理, EGLD, ESDT, 区块链开发, 代币验证, 支付路由。

智能合约 0 次安装 0 次浏览 更新于 3/21/2026

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(),
    "错误代币"
);